awk 在*系统调用*中处理带有特殊字符的文件名

awk processing filenames with special characters in *system call*

我正在处理大量 md5, dir/filename 对。我需要将文件大小插入到列表中以制作 md5, size, dir/filename 三元组列表。

数据文件的相关片段是:

file MD5sum-stage1A.txt
...
d9c6be18d35619c7532f9c94f5a9bf58  /mnt/dir1/dir2/branch1/04 05 Custom .mp4
01c0fadb91c8ef0815a7753ad25a8c1c  /mnt/dir1/dir2/branch1/branch2/Using the -proc directory and the $$ Variable.odt
...
EOF

第二条数据线有问题,文件名中有$$

除了一个例外,代码可以正常工作。 以下是到目前为止的代码:

someone@system01:~/tmp$ awk 'NR==15522, NR==15523 {
> md5=
> file=substr([=11=],35)
> size="###"
> cmd=sprintf("stat --format=%s \"%s\"", "%s",file)
> cmd | getline size
> close(cmd)
> printf "%s\t%s\t%s\n",md5, size, file
> }' MD5sum-stage1A.txt

d9c6be18d35619c7532f9c94f5a9bf58    6747587 /mnt/dir1/dir2/dir3/04 05 Custom .mp4
stat: cannot stat '/mnt/dir1/dir2/Using the -proc directory and the 20483 Variable.odt': No such file or directory
01c0fadb91c8ef0815a7753ad25a8c1c    ### /mnt/dir1/dir2/Using the -proc directory and the $$ Variable.odt

到目前为止,代码正在处理 shell 的细微差别,并处理文件名中的空格和大多数字符。 但是,shell 似乎想要用 processid 替换“$$”。

使用 Awk 如何减轻这种行为?

MD5和文件大小一起生成更容易

例如使用 find 定位文件并使用 awk 格式化输出:

find /mypath -type f -exec md5sum {} \; -exec stat -c %s {} \; | \
  awk  'NF==1{print hash,,rest;next}{hash=;rest=substr([=10=],35)}'

如果不能生成MD5,需要重新使用文件,可以避免awk,用bash read命令:

while read hash f; do 
   echo "$hash $(stat -c %s "$f") $f"
done < file

我会这样做:使用 xargs 一次读取一行,然后使用 cut -d" " 分割该行以剪切输入的部分:

xargs -n1 -d $'\n' bash -c 'f=$(cut -d" " -f3- <<<""); printf "%s\t%s\t%s\n" "$(cut -d" " -f1 <<<"")" "$(stat -c %s "$f")" "$f"' --

或者用双空格和换行符替换一些您可以轻松处理的不可读字符(如 0x01 字节):

sed 's/  /\x01/' | tr '\n' '' |
xargs -n2 -d $'\x01' bash -c 'printf "%s\t%s\t%s\n" "" "$(stat -c %s "")" ""' --

甚至用双空格代替一些不可读的字符:

sed 's/  /\x01/' |
while IFS=$'\x01' read -r md5 file; do
     printf "%s\t%s\t%s\t\n" "$md5" "$(stat -c %s "$file")" "$file" 
done

问题是您从 awk 调用 stat 时使用的引号与从 shell 调用 stat 时使用的引号不同——如果您从 shell 调用 stat 时使用与您相同的引号在 awk 命令中使用你会得到同样的错误。看:

$ ls
'foo $$ bar'

$ stat --format=%s "foo $$ bar"
stat: cannot stat 'foo 1913 bar': No such file or directory

现在有了正确的引用:

$ stat --format='%s' 'foo $$ bar'
6

在您的 awk 命令中,您当前使用的引号不正确:

awk -v file='foo $$ bar' 'BEGIN{cmd=sprintf("stat --format=%s \"%s\"", "%s",file); cmd | getline size; close(cmd); print file, size}'
stat: cannot stat 'foo 2523 bar': No such file or directory
foo $$ bar

但如果您使用与正确引用的 shell 命令中相同的引号:

awk -v file='foo $$ bar' 'BEGIN{cmd=sprintf("stat --format=7%s7 7%s7", "%s",file); cmd | getline size; close(cmd); print file, size}'
foo $$ bar 6

您可以在格式字符串中打印文字 %,只需将其加倍顺便说一下:

awk -v file='foo $$ bar' 'BEGIN{cmd=sprintf("stat --format=7%%s7 7%s7",file); cmd | getline size; close(cmd); print file, size}'
foo $$ bar 6

下面是我真正处理这个问题的方法:

$ ls -1
'an other file'
files
'foo $$ bar'

$ cat files
12345   an other file
987     foo $$ bar

$ cut -f2- files | xargs -d'\n' stat --format='%s'
5
6

$ cut -f2- files | xargs -d'\n' stat --format='%s' |
    awk -v OFS='\t' 'NR==FNR{size[NR]=[=15=]; next} {print size[FNR], [=15=]}' - files
5       12345   an other file
6       987     foo $$ bar

非常感谢您的回答,他们给了我一些想法并希望简化和优化初始任务。因此,本着分享我最终得到的东西的精神,也许它会对其他人有用,是:

find /media/BACKUPS/foo -not -path '*/\.*' -type f -exec md5sum {} \; -printf '%s\n' | awk 'NF==1{printf "%s\t%s\t%s\n",hash,,rest;next}{hash=;rest=substr([=10=],index([=10=],))}'  >  MD5sum-dataset-foo.txt

再一次,非常感谢...