我想为 bash 创建一个实用函数来删除重复的行。我正在使用功能
function remove_empty_lines() {
if ! command -v awk &> /dev/null
then
echo '[x] ERR: "awk" command not found'
return
fi
if [[ -z "$1" ]]
then
echo "usage: remove_empty_lines <file-name> [--replace]"
echo
echo "Arguments:"
echo -e "\t--replace\t (Optional) If not passed, the result will be redirected to stdout"
return
fi
if [[ ! -f "$1" ]]
then
echo "[x] ERR: \"$1\" file not found"
return
fi
echo $0
local CMD="awk '!seen[$0]++' $1"
if [[ "$2" = '--reload' ]]
then
CMD+=" > $1"
fi
echo $CMD
}
Run Code Online (Sandbox Code Playgroud)
如果我直接运行主 awk 命令,它就可以工作。但是当我在函数中执行相同的 $CMD 时,我收到此错误
$ remove_empty_lines app.js
/bin/bash
awk '!x[/bin/bash]++' app.js
Run Code Online (Sandbox Code Playgroud)
原始代码在几个方面被破坏:
--reload,它会在awk读取这些内容之前截断输出文件的内容(请参阅如何在命令中使用文件并将输出重定向到同一个文件而不截断它?)eval; BashFAQ #48来解决其中的一些问题;BashFAQ #48描述了为什么这样做会引入安全性错误)。return在$?为零的情况下,错误情况用偶数处理;这意味着它return本身将返回零/成功/真实状态,不会向调用者透露发生了任何错误。据推测,您将输出存储在其中的原因CMD是能够有条件地执行重定向,但这可以通过其他方式完成:下面,我们始终创建一个文件描述符out_fd,但将其指向stdout(在没有调用时--reload)或指向临时文件(如果用 调用--reload);if-and-only-if awk 成功,然后我们将临时文件移动到输出文件上,从而将其替换为原子操作。
remove_empty_lines() {
local out_fd rc=0 tempfile=
command -v awk &>/dev/null || { echo '[x] ERR: "awk" command not found' >&2; return 1; }
if [[ -z "$1" ]]; then
printf '%b\n' >&2 \
'usage: remove_empty_lines <file-name> [--replace]' \
'' \
'Arguments:' \
'\t--replace\t(Optional) If not passed, the result will be redirected to stdout'
return 1
fi
[[ -f "$1" ]] || { echo "[x] ERR: \"$1\" file not found" >&2; return 1; }
if [ "$2" = --reload ]; then
tempfile=$(mktemp -t "$1.XXXXXX") || return
exec {out_fd}>"$tempfile" || { rc=$?; rm -f "$tempfile"; return "$rc"; }
else
exec {out_fd}>&1
fi
awk '!seen[$0]++' <"$1" >&$out_fd || { rc=$?; rm -f "$tempfile"; return "$rc"; }
exec {out_fd}>&- # close our file descriptor
if [[ $tempfile ]]; then
mv -- "$tempfile" "$1" || return
fi
}
Run Code Online (Sandbox Code Playgroud)