2017-01-05 6 views
0

У меня есть файл журнал, который выглядит следующим образом:Баша список файлов обновления имен файлов с полным путем

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 
+1

[DontReadLinesWithFor] (HTTP : //mywiki.wooledge.org/DontReadLinesWithFor) –

+1

... и не пытайтесь использовать 'ls' программно: http://mywiki.wooledge.org/ParsingLs –

+1

И не используйте' >> 'внутри цикл - это * много * более эффективно размещать '> outfile' на * вне * цикла, поэтому вы открываете файл только один раз, а также повторно открываете его каждый раз, когда хотите добавить строку. –

ответ

3
# 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 '[email protected][]*?[]@\\&@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 
+0

Этот скрипт отлично работал, как только я удалил -quit из переменной fullname. Не знаю, почему это предотвратило заполнение переменной – bdamon

+0

@bdamon, ooh - понадобилось '-print' перед' -quit'. Если у вас нет '-quit', кстати, тогда у вас будут ошибки, если есть два файла с тем же именем, поскольку оба имени будут сохранены в переменной с новой линией между ними. –

+0

Еще раз спасибо @Charles Duffy! Будут случаи, когда есть файлы с тем же именем. Кстати, сайт орфографии отлично, что мне действительно нужно! – bdamon

0

В 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 critizices в комментариях (++ для него). Оставшись здесь для образовательных целей:

$ 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 командной строки собирается в вар s
  • , который выполняется и вывод записывается обратно в последнем поле ($NF)
+0

Рассмотрите, что произойдет, если у вас есть загруженное имя файла, содержащее '$ (rm -rf $ HOME)' в нем. Подстановка данных в сгенерированный код - это почти самое определение того, как получить инъекционные уязвимости (будь то инъекция оболочки - как здесь - или SQL-инъекция), и это именно то, что здесь делается. –

+0

... ну, честно говоря, поскольку мы берем только последнее поле, '$ (rm -rf $ HOME)' будет рассматривать только как '$ HOME)', но тогда это еще одна ошибка сама по себе , Команды с одним словом (без аргументов) тривиально исполняются таким образом ('$ (/ home/uploads/run-my-script)') или серия с разделителями с запятой; выясняя, что именно это можно сделать, это интересное упражнение для злоумышленника, но в любом случае это опасное свойство иметь для чего-то, что будет обрабатывать загруженные файлы с неизвестными именами. –

+0

Мне пришлось немного подумать о том, как выполнить произвольное выполнение команды, но файл с именем '../test/file/$([email protected];[email protected]@$HOME;$payload).txt 'будет делать трюк - нет пробелов, поэтому он будет работать даже с' $ NF', обрезая все, кроме последнего сегмента сегмента, разделенного пробелами. –

 Смежные вопросы

  • Нет связанных вопросов^_^