【问题标题】:how can i add to a file from a scalar in bash?如何从 bash 中的标量添加到文件?
【发布时间】:2020-06-10 13:10:47
【问题描述】:

我正在编写一个函数,该函数将接受 2 个参数——一个标量的名称和一个文件名,并将标量(以换行符分隔)写入该文件。我收到“错误替换”错误。这是我的代码:

#!/bin/bash

arr_to_f() {
for y in "${1[@]}"
       do
       echo "$y" >> "$2"
       done
}

mapfile -t arr < <(echo "one" ; echo "two" ; echo "three")

arr_to_f arr file

我可以使用我的循环在函数之外执行此操作,但我认为我会经常使用它,所以如果可能的话,我宁愿调用它。

【问题讨论】:

标签: bash


【解决方案1】:

您可以创建对 arr 变量的命名引用。我还在您的代码中将echo 替换为printf,请参阅Why is printf better than echo?

#!/bin/bash

arr_to_f() {
  local -n myarray=$1
  for y in "${myarray[@]}"; do
    printf '%s\n' "$y"
  done >>"$2"
}

mapfile -t arr < <(printf '%s\n' one two three)
arr_to_f arr file

你也可以在你的函数中去掉 for 循环:

arr_to_f() {
  local -n myarray=$1
  printf '%s\n' "${myarray[@]}" >>"$2"
}

【讨论】:

  • 一般来说,这正是我自己会写的。不过,我建议的一项更改是将每个 printf 之后的 &gt;&gt; "$2" 替换为 done 之后的一个 &gt;"$2"。这样,我们在整个循环中保持文件句柄打开,而不是一遍又一遍地重新打开它(因此也减少了文件系统负载,因为每个 close() 更新 inode 的 mtime,而不仅仅是数据内容),并且还可以继续添加新行,而不会产生保留旧内容的意外副作用。
  • 谢谢@CharlesDuffy。我在done 之后移动了&gt;&gt;"$2",我认为这是你的主要观点。我将重定向保留为“追加”,因为我不知道 OP 是否要覆盖或追加。
【解决方案2】:

注意:此答案使用eval,应尽可能避免使用。请参阅Freddy's answer,它使用declare -n / local -n 创建命名引用。

如果您使用的是旧版本的 bash,则可能需要这种方法。



正如已经指出的那样,您传递的是文字字符串 arr,这不是一个数组,因此是“错误替换”。

您可以使用eval 构建一个字符串,从而扩展为您所追求的:

arr_to_file() {
    local cmd
    printf -v cmd 'local -a _content=( "${%q[@]}" )' "${1}"
    eval "${cmd}"
    printf '%s\n' "${_content[@]}" > "${2}"
}

mapfile -t arr < <(echo "one" ; echo "two" ; echo "three")

arr_to_file arr file

eval 通常会收到一个很大的警告通知,所以这是你的:“如果你不信任你给它的字符串,eval 是非常危险的”。

如果我们对eval 非常小心,那么它可以安全地使用。这里printf "%q" "${1}" 用于引用适合shell 输入的第一个参数(例如:printf '%q' '}' 不会脱离变量的名称)。感谢Charles Duffy 提供了经过修订的、更安全、更易读的 sn-p。

这种方法也适用于关联数组(即:使用键而不是索引的数组),但输出将是无序的。

【讨论】:

    猜你喜欢
    • 2020-07-23
    • 2019-02-02
    • 1970-01-01
    • 2023-01-22
    • 2015-11-01
    • 2012-01-30
    • 1970-01-01
    • 2014-08-29
    • 2013-05-08
    相关资源
    最近更新 更多