使 awk 命令就地编辑文件的 bash 脚本不起作用

使 awk 命令就地编辑文件的 bash 脚本不起作用

我有以下 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您应该对选项参数进行更多验证,以确保您期望的自然数之类的值确实是自然数。

相关内容