Как посмотреть 17-ю (или последнюю, если меньше) строку в файлах папки?

8

Я сейчас пользуюсь

watch head -n 17 *

который работает, но также показывает все строки до 17-го. По сути, я хотел бы показать только последнюю строку для каждого файла, который отображается с моим текущим подходом. Как я могу этого достичь?

пример

Для примера давайте уменьшим строку nr. до 7. Итак:

Пример файла:

1
2
3
4
5
6
7
8

эта строка:

watch head -n 7 *

выходы

1
2
3
4
5
6
7

где я хочу:

7
Феликс Домбек
источник

Ответы:

15

С 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

Но это было бы намного менее эффективно, так как подразумевает выполнение нескольких команд на файл.

Стефан Шазелас
источник
Спасибо за примечание безопасности. На самом деле вы правы, и моя команда также не сделала то, что мне было нужно, то есть просмотр добавленных файлов, которые также решает ваша версия. Единственным недостатком является то, что мне нужно учить AWK, хотя у меня было это, find . -execнапример, так: D
Феликс Домбек
11

Объедините headи tailкак в этих двух примерах:

$ seq 1 80 | head -n 17 | tail -n 1
17

$ seq 1 10 | head -n 17 | tail -n 1
10

Итак, чтобы решить вашу актуальную проблему, команда:

watch 'for f in *; do head -n 17 -- "$f" 2>/dev/null | tail -n 1 ; done'

Обратите внимание на 2>/dev/nullчасть, это необходимо, потому что * будет соответствовать каталогам и любым файлам, для которых у вас нет прав на чтение, что приведет к сообщению об ошибке, которое вы, вероятно, захотите скрыть.

Хайд
источник
0

другой подход с find, exec и sed

watch find . -type f -name \"*\" -exec sh -c \'\( printf '%-50s ' {} \; sed -n 17p {}\)\' \\\;
dhanlin
источник
1
Не работает с файлами длиной менее 17 строк. Внимательно прочитайте вопрос.
Wildcard