![使 awk 命令就地编辑文件的 bash 脚本不起作用](https://linux22.com/image/225801/%E4%BD%BF%20awk%20%E5%91%BD%E4%BB%A4%E5%B0%B1%E5%9C%B0%E7%BC%96%E8%BE%91%E6%96%87%E4%BB%B6%E7%9A%84%20bash%20%E8%84%9A%E6%9C%AC%E4%B8%8D%E8%B5%B7%E4%BD%9C%E7%94%A8.png)
我有以下 if-else 语句,用于检查 i 标志是否已添加到命令中,如果是,则从 stdin 写入文件:
29 if [ $inplace -eq 1 ]; then
30 tmp_file=$(mktemp)
31 awk -v num="^$number" -v col="$column" -v value="$value" -F ',' \
32 'BEGIN{OFS=FS}{ if ($0 ~ num) { $col=value; print } else print}' "$file" > "$tmp_file"
33 mv "$tmp_file" "$file"
34 else
35 awk -v num="^$number" -v col="$column" -v value="$value" -F ',' \
36 'BEGIN{OFS=FS}{ if ($0 ~ num) { $col=value; print } else print}' "$file"
37 fi
但我收到以下错误:
mv: cannot move '/tmp/tmp.ahALZC2Hgh' to '': No such file or directory
这意味着 $file 是空的,这对我来说很有意义,因为我在某处读到 stdin 只能使用一次并且它被上面的 awk cmd 使用
有人知道如何保值吗? (不做类似: var=$(cat) 的事情,因为我听说这是不好的做法?)
或者,如果这是一个坏主意,因为并且不应该实施,那也可以!
运行该文件并收到错误的命令是:
cat data.csv | ./update.sh -n 444 -c 2 -v "Alex" -i
i 标志用于用户表示用户想要对原始文件进行更改,在这种情况下,变量 $inplace 在以下情况的 while 循环中设置为 1:
3 while getopts f:n:c:v:i opt; do
4 case $opt in
5 f) file="$OPTARG";;
6 n) number="$OPTARG";;
7 c) column="$OPTARG";;
8 v) value="$OPTARG";;
9 i) inplace=1;;
10 *) printf>&2 '%s\n' "Usage: $0 [-f file] [-n number] [-c column] [-v value]";;
11 esac
12 done
整个脚本如下:
#!/bin/bash
while getopts f:n:c:v:i opt; do
case $opt in
f) file="$OPTARG";;
n) number="$OPTARG";;
c) column="$OPTARG";;
v) value="$OPTARG";;
i) inplace=1;;
*) printf>&2 '%s\n' "Usage: $0 [-f file] [-n number] [-c column] [-v value]";;
esac
done
if [ -z "$column" ]; then
echo "Please specify a column number with -c." >&2
exit 1
elif [ -z "$number" ]; then
echo "Please specify a phone number to filter by via -n" >&2
exit 1
elif [ -z "$value" ]; then
echo "Please specify the new value via -v" >&2
exit 1
elif [ "$column" -gt 4 ] || [ "$column" -lt 1 ]; then
echo "this script can only handle cols1-4. for group editing, try ./groups.sh" >&2
exit 1
fi
if [ $inplace -eq 1 ]; then
tmp_file=$(mktemp)
awk -v num="^$number" -v col="$column" -v value="$value" -F ',' \
'BEGIN{OFS=FS}{ if ($0 ~ num) { $col=value; print } else print}' "$file" > "$tmp_file"
mv "$tmp_file" "$file"
else
awk -v num="^$number" -v col="$column" -v value="$value" -F ',' \
'BEGIN{OFS=FS}{ if ($0 ~ num) { $col=value; print } else print}' "$file"
fi
exit 0
答案1
- 关于你的陈述“所以在这种情况下,一个简单的
-i filename
选择将是最好的选择” - 不,最好的选择是用户做cat foo | command > filename
,而不是cat foo | command -i filename
。 - 没有对标准输入进行“就地”编辑,您只是将标准输出定向到一个文件,这就是
> filename
已经做的事情。 - 您的 -i 选项不应包含文件名参数,这没有意义并且会使您的代码复杂化。如果没有输入文件并且有人使用 -i 调用您的脚本,则将其报告为错误,就像 sed 一样,例如尝试,
seq 3 | sed -i 's/2/4/'
它会告诉您sed: no input files
- 正如所写,如果 awk 遇到故障,您的代码将丢弃您的输入文件。使用:
> "$tmp_file" &&
mv "$tmp_file" "$file"
代替:
> "$tmp_file"
mv "$tmp_file" "$file"
{ if ($0 ~ num) { $col=value; print } else print }
={ if ($0 ~ num) { $col=value } print }
可以惯用地写为$0 ~ num { $col=value } 1
.- 您已经删除了我再次提供的移位命令,从而使其余代码更难再次编写。
- 您不应该有 -f 文件选项,您想要用作输入的任何文件都应该位于 getopts 循环中正在处理的选项+参数之后,并由“$@”寻址。
- 另请参阅其他的建议你收到。
尝试这个(未经测试)作为起点:
$ cat tst.sh
#!/usr/bin/env bash
tmp_file=$(mktemp) || exit 1
trap 'rm -f "$tmp_file"; exit' EXIT
while getopts n:c:v:i opt; do
case $opt in
n) number="$OPTARG";;
c) column="$OPTARG";;
v) value="$OPTARG";;
i) inplace=1;;
*) printf>&2 '%s\n' "Usage: $0 [-f file] [-n number] [-c column] [-v value]";;
esac
done
shift $((OPTIND-1))
if [[ -z "$column" ]]; then
echo "Please specify a column number with -c." >&2
exit 1
elif [[ -z "$number" ]]; then
echo "Please specify a phone number to filter by via -n" >&2
exit 1
elif [[ -z "$value" ]]; then
echo "Please specify the new value via -v" >&2
exit 1
elif ! (( 1 <= column )) && (( column <= 4 )); then
echo "this script can only handle cols1-4. for group editing, try ./groups.sh" >&2
exit 1
fi
xform() {
awk -v num="^$number" -v col="$column" -v value="$value" \
'BEGIN{FS=OFS=","} $0 ~ num{$col=value} 1' "$@"
}
if [[ -n "$inplace" ]]; then
if (( $# == 0 )); then
echo "cannot do inplace editing as requested by -i without an input file present" >&2
exit 1
fi
all_writeable=1
for file; do
if [[ ! -w "$file" ]]; then
echo "file \"$file\" is not writeable" >&2
all_writeable=0
fi
done
if (( all_writeable == 0 )); then
exit 1
fi
for file; do
xform "$file" > "$tmp_file" &&
mv "$tmp_file" "$file"
done
else
xform "$@"
fi
column
您应该对选项参数进行更多验证,以确保您期望的自然数之类的值确实是自然数。