我想实现和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