如何在没有awk的情况下打印一列到其他列的组合路径?

如何在没有awk的情况下打印一列到其他列的组合路径?

我想实现和awk命令一样的功能:

awk  -F ";" '{print $3 >  "/" $1 "/" $2}' file

例如:

我有一个包含两行作为输入的文件:

path1;filename_1;f3_1
path2;filename_2;f3_2   

我想要的输出为:

  • 在文件中/path1/filename_1,内容是f3_1
  • 在文件中/path2/filename_2,内容是f3_2

当我使用 awk 执行此操作时,我得到了 141 退出代码 SIGPIPE。我只是想绕过这个错误。

答案1

听起来awk文件句柄用完了。如果您无法升级到较新版本的awk,最明显的解决方案是在使用文件后关闭它们。

是否有任何 path;filename组合可能出现多次?

如果没有,请尝试:

awk  -F ";" '{f="/"$1"/"$2; print $3 > f ; close(f)}' file

否则,>> f如果可以附加到现有文件,则使用:

awk  -F ";" '{f="/"$1"/"$2; print $3 >> f ; close(f)}' file

如果文件在第一次写入时需要被截断,但从那时起追加,则情况会稍微复杂一些:

awk  -F ";" '{ f="/"$1"/"$2;
               if ( !fnames[f]++ ) { print > f };
               print $3 >> f;
               close(f)
             }' file

关联fnames数组用于确定脚本是否已经看到该文件名。如果不是,它会截断该文件。

答案2

在我看来,这样做是愚蠢的,因为它与的能力awk完美匹配。awk你可以这样做(bash-4.3语法):

(unset fds; typeset -A fds
while IFS=';' read -r a b c rest; do
  file=/$a/$b
  [[ ${fds[$file]} ]] || exec {fds[$file]}> "$file"
  printf '%s\n' "$c" >&"${fds[$file]}"
done < file)

但这会降低效率(另请参阅为什么使用 shell 循环处理文本被认为是不好的做法?),与一些可以解决这个问题的实现相反awk,如果文件太多,将使您遇到并发打开文件数量的限制。

或者,您可以这样做(POSIXsh语法):

while IFS=';' read -r a b c rest; do
  printf '%s\n' "$c" >> "/$a/$b"
done < file

但这会将文本附加到文件而不是覆盖它们(并且仍然比使用效率低很多awk)。

您也可以这样做,perl但是,您再次需要完成awk打开文件并手动维护 fd 列表的工作:

perl -F ';' -lane '
  $file = "/$F[0]/$F[1]";
  unless (defined $fds{$file}) {
    open $fds{$file}, ">", $file or die "$file: $!\n";
  }
  print {$fds{$file}} $F[2]' < file

(同样存在与同时打开文件限制相同的潜在问题)。

答案3

使用剪切:

while read -r line  ; do printf "in the file /%s/%s the content is %s\n" $(echo $line | cut -d';' -f1) $(echo $line | cut -d';' -f2) $(echo $line | cut -d';' -f3) ; done < file

还有更短的版本(从 Stéphane Chazelas 的回答中窃取的一些内容)并将输出重定向到另一个文件以保存结果:

while IFS=';' read -r f1 f2 f3  ; do printf "in the file /%s/%s the content is %s\n" $f1 $f2 $f3 ; done < files > file2

相关内容