替换文件中的字符

替换文件中的字符

您好,我正在编写脚本,该脚本需要 2 个符号并用第二个符号替换第一个符号,并且该脚本也将其作为参数文件。想法是将文件中的第一个字符替换为第二个字符,这里是脚本:

char_src=$1
char_dest=$2
shift

for file
do
    while read -r line
    do
        line=${line//$char_src/$char_dest}
        echo "$line"
    done < "$file"
done

问题是它打印了正确的内容,但它没有保存对文件的更改,我尝试用 done < "$file"

答案1

首先是简单的部分:从 shell 中逐行读取文件很慢,您可能想使用它trman tr详情请参阅。

其次,您需要一个临时文件。您不能同时读取和写入文件(无论如何不能从 shell 读取和写入文件)。所以你需要做这样的事情:

tr -- "$1" "$2" <"$file" >"$file".tmp
mv -f -- "$file".tmp "$file"

一个明显的问题是,如果tr由于任何原因(比如因为$1是空的)而失败,会发生什么。然后$file.tmp仍然会被创建(它是在 shell 解析该行时创建的),然后$file被它替换。

因此,一种更安全的方法如下所示:

tr -- "$1" "$2" <"$file" >"$file".tmp && \
mv -f -- "$file".tmp "$file"

现在只有成功$file时才被替换。tr

但是如果周围有另一个文件命名怎么办$file.tmp?好吧,它会被覆盖。这就是事情变得更加复杂的地方:技术上正确的方法是这样的:

tmp="$( mktemp -t "${0##*/}"_"$$"_.XXXXXXXX )" && \
trap 'rm -f "$tmp"' EXIT HUP INT QUIT TERM || exit 1
tr -- "$1" "$2" <"$file" >"$tmp" && \
cat -- "$tmp" >"$file" && \
rm -f -- "$tmp"

这里mktemp创建一个临时文件;trap确保如果脚本退出(正常或异常),该文件被清理;cat -- "$tmp" >"$file"使用and代替来mv确保$file保留 的权限。如果您没有 的写入权限$file,但临时文件最终会被删除,这仍然可能会失败。

或者,GNUsed可以选择就地编辑文件,因此您可以执行以下操作:

sed -i "s/$1/$2/g" "$file"

然而,这并不像看起来那么简单。如果$1$2对于 具有特殊含义,则上述操作将失败sed。所以你需要先逃避它们:

in=$(  printf '%s\n' "$1" | sed 's:[][\/.^$*]:\\&:g' )
out=$( printf '%s\n' "$2" | sed 's:[\/&]:\\&:g;$!s/$/\\/' )
sed -i "s/$in/$out/" "$file"

答案2

现在忽略这样一个事实:有专门为此目的而构建的工具。

char_src=$1
char_dest=$2
shift

您可能想要shift 2在这里,一个普通的文件shift会移动$2$1并留下第一个文件名。

    while read -r line

您正确使用read -r,但请注意,使用默认值IFS,read将删除前导和尾随空格。

    done < "$file"

<仅重定向输入,要重定向输出,您需要> "$file2", 并且如许多地方所述(例如这里),重定向到同一文件只会在读取任何内容之前截断它。

关于为此制作的工具,要将单个字符更改为其他单个字符,请使用tr。 shell 的${var//pat/repl}构造替换了整个字符串,并且更类似于s/pat/repl/gin sed

相关内容