【问题标题】:bash update file list of filenames with full pathbash 更新具有完整路径的文件名的文件列表
【发布时间】:2017-05-20 08:22:49
【问题描述】:

我有一个如下所示的日志文件:

 Jan 1 06:09:23 somefile.txt
 Jan 2 12:18:27 somefile1.txt
 Jan 3 04:16:00 somefile2.txt

我想找到每个文件并将每个文件的完整路径插入到这个文件中。我认为 find、awk 和 sed 的某种组合可以实现这一点,但到目前为止,我还没有提出一个可行的解决方案来更新文件,使其如下所示。

Jan 1 06:09:23 /path/to/file/somefile.txt
Jan 2 12:18:27 /path/to/file1/somefile1.txt
Jan 3 04:16:00 /path/to/file2/somefile2.txt

我已经能够删除文件名并毫无问题地找到文件,但到目前为止我想出的内容会写出一个新文件并丢失原始文件内容。我曾希望保持原始文件完好无损。

#!/bin/bash
#functions
getup(){

for i in `cat /home/work/uploadtmp`
do
     find /home/uploads/*$i 2> /dev/null >> /home/work/upfile
done
}

listfile(){
while read line; do ls -lt $line; done < /home/work/upfile

}

#run functions
getup
listfile | awk '{print $1 " " $2 " " $3 " " $4}' | sort -k1M -k2 -k3 > /home/log/newfile

【问题讨论】:

  • ...不要尝试以编程方式使用ls:mywiki.wooledge.org/ParsingLs
  • 并且不要在循环中使用&gt;&gt;——将&gt;outfile放在外部效率更高循环,所以你只打开一次文件,而不是每次你想追加一行时重新打开它。
  • 这是一个 GNU 系统吗?你有readlink -f 可用吗?
  • 哪个文件,究竟有问题的格式? uploadtmp?还是您希望 ls -lt 的输出采用这种格式?

标签: bash awk sed find


【解决方案1】:
# create a temporary output file, so we only overwrite the destination when complete
tempfile=$(mktemp /home/log/newfile.XXXXXX)

# ...and tell the shell to delete that temporary file if it's still around when we exit
# ...won't work for SIGKILL or power failures, but better than nothing.
trap 'rm -f -- "$tempfile"' EXIT

# iterate over lines in the input file...
while read -r mon day time filename; do
  # ...quoting each name to only match itself...
  filename_pat=$(sed -e 's@[]*?[]@\\&@g'  <<<"$filename")
  # ...using find to locate the first file with the given name for each...
  fullname=$(find /home/uploads -name "$filename_pat" -print -quit)
  # ...and printing that new name on our stdout
  printf '%s\n' "$mon $day $time $fullname"
done </home/work/uploadtmp >"$tempfile" # ...redirecting the whole loop to our tempfile...

# ...then performing a single atomic rename to overwrite the final destination
mv "$tempfile" /home/log/newfile

【讨论】:

  • 当我从 fullname 变量中删除 -quit 后,该脚本运行良好。不知道为什么这会阻止变量被填充
  • @bdamon,哦——在-quit 之前需要一个-print。顺便说一句,如果你没有-quit,那么当有两个同名文件时你会遇到错误,因为这两个文件最终都会存储在变量中,并且它们之间有一个换行符。
  • 再次感谢@Charles Duffy!有时会有同名的文件。顺便说一句,拼写检查网站很棒,我真的需要!
  • 公平地说,顺便说一句,在这种情况下我们仍然会有错误,只是 不同 错误 -- 寻找 foo.txt,我们目前正在找到 first 一个,而不是 最新 一个或具有特定上传时间的一个。最好的办法是让任何生成 uploadtmp 的软件实际生成合格的路径名,而不是试图事后重建它们。
  • (另外,@JamesBrown 在回答中批评安全漏洞提醒我要仔细查看我自己的,因此在将它们传递给find 之前更改为转义名称 - 这样一个名为 @ 的文件987654328@ [在其名称中带有文字 *] 导致 find 查找具有该名称的文件,而不是具有 .txt 扩展名的任何文件。
【解决方案2】:

在awk中,外部使用find收集文件路径:

$ cat program.awk
NR==FNR {                 # read in the files file records
    a[$NF]=$0; next }         # hash them to a and tskip o the next record
{                         # find produced list processing
    n=split($0,b,"/");        # basename functionality, filename part in b[n]
    if(sub(b[n],$0,a[b[n]]))  # replace filename in a with full path version
        print a[b[n]]         # and print
}
$ awk -f program.awk files <(find .)
Jan 3 04:16:00 ./file2/somefile2.txt
Jan 1 06:09:23 ./file/somefile.txt
Jan 2 12:18:27 ./file1/somefile1.txt

此解决方案(或旧解决方案)不允许文件名中有空格。不过,通过放弃 $NF 用法,在第一个块中很容易解决:

f=$0                      # current record to var f
sub(/^([^ ]+ ){3}/,"",f)  # remove timestamp
a[f]=$0                   # hash to a on f
next                      # ...

旧版本 @CharlesDuffy 在 cmets 中批评(++ 表示)。出于教育目的而留在这里:

$ awk -v path=".." '{ s="find " path " -name " $NF; s | getline $NF } 1' file
Jan 1 06:09:23 ../test/file/somefile.txt
Jan 2 12:18:27 ../test/file1/somefile1.txt
Jan 3 04:16:00 ../test/file2/somefile2.txt
  • find 命令字符串被收集到 var s
  • 执行并输出写回最后一个字段($NF)

【讨论】:

  • 考虑一下,如果您上传的文件名中包含 $(rm -rf $HOME),会发生什么情况。将数据替换为生成的代码几乎就是一个人如何获得注入漏洞的定义(无论是 shell 注入 - 就像这里一样 - 或 SQL 注入),而这正是这里所做的。
  • ...好吧,公平地说——因为我们只取最后一个字段,$(rm -rf $HOME) 只会解析为$HOME),但是,这本身就是另一个错误。一字命令(不带参数)可以通过这种方式($(/home/uploads/run-my-script))或分号分隔的系列轻松执行;对于攻击者来说,弄清楚究竟可以做什么是一项有趣的练习,但无论如何,对于将要处理名称未知的上传文件的东西来说,拥有它是一个危险的属性。
  • 我不得不考虑一下如何执行任意命令,但是一个名为 ../test/file/$(IFS=@;payload=rm@-rf@$HOME;$payload).txt 的文件可以解决问题——没有空格,所以即使使用 $NF 也可以。修剪除最后一个以空格分隔的路径段之外的所有内容。
猜你喜欢
  • 2014-09-26
  • 2011-07-14
  • 1970-01-01
  • 1970-01-01
  • 2022-01-09
  • 2011-01-08
  • 2014-04-25
  • 2011-04-19
  • 1970-01-01
相关资源
最近更新 更多