С GNU awk
:
watch -x gawk '
FNR == 17 {nextfile}
ENDFILE {if (FNR) printf "%15s[%02d] %s\n", FILENAME, FNR, $0}' ./*
Который дает вывод как:
./file1[17] line17
./short-file2[05] line 5 is the last
Обратите внимание, что ./*
глобус расширяется только один раз в тот момент, когда watch
вызывается.
Ваша watch head -n 17 *
уязвимость была связана с произвольным введением команд, поскольку расширение, которое *
фактически интерпретировалось оболочкой как код оболочки, watch
вызывает интерпретацию объединения аргументов с пробелами.
Если $(reboot)
в текущем каталоге был вызван файл , он перезагрузился бы.
С помощью -x
мы говорим watch
пропустить оболочку и выполнить команду напрямую. В качестве альтернативы вы можете сделать:
watch 'exec gawk '\''
FNR == 17 {nextfile}
ENDFILE {if (FNR) printf "%15s[%02d] %s\n", FILENAME, FNR, $0}'\'' ./*'
Для watch
запуска оболочки, которая будет расширять этот ./*
шар на каждой итерации. watch foo bar
в действительности так же, как watch -x sh -c 'foo bar'
. При использовании watch -x
вы можете указать, какая оболочка вам нужна, и, например, выбрать более мощную, например zsh
, такую, которая может выполнять рекурсивное сглаживание и ограничиваться обычными файлами:
watch -x zsh -c 'awk '\''...'\'' ./**/*(.)'
Без gawk
, вы все еще можете сделать что-то вроде:
watch '
for file in ./*; do
[ -s "$file" ] || continue
printf "%s: " "$file"
head -n 17 < "$file" | tail -n 1
done'
Давать выходной как:
./file1: line17
./short-file2: line 5 is the last
Но это было бы намного менее эффективно, так как подразумевает выполнение нескольких команд на файл.
find . -exec
например, так: DОбъедините
head
иtail
как в этих двух примерах:Итак, чтобы решить вашу актуальную проблему, команда:
Обратите внимание на
2>/dev/null
часть, это необходимо, потому что * будет соответствовать каталогам и любым файлам, для которых у вас нет прав на чтение, что приведет к сообщению об ошибке, которое вы, вероятно, захотите скрыть.источник
другой подход с find, exec и sed
источник