Прочитайте первую строку всех файлов txt в папке с подпапками и напечатайте имя файла и первую строку в другом файле

Рассмотрим следующий пример скрипта (все отступы являются буквальными вкладками):

#!/bin/sh

var='hello'

cat >script1.sh <<-END_SCRIPT1
        #!/bin/sh
        echo 'This is script1 ($var)'
        printf '\$0 is "%s"\n' "\$0"

        cat >script2.sh <<END_SCRIPT2
                #!/bin/sh
                echo 'This is script2 ($var)'
                printf '\\\$0 is "%s"\n' "\\\$0"
        END_SCRIPT2
END_SCRIPT1

Во внешнем здесь -документе нам нужно экранировать $в "$0"как "\$0", чтобы он не расширялся сценарием, записывающим документ.

Чтобы иметь возможность записать внутренний здесь -документ, мы должны убедиться, что внешний здесь -документ пишет "\$0". Обратная косая черта должна быть экранирована как \\, что превращает строку в "\\\$0".

Сценарий выше записывает script1.shкак

#!/bin/sh
echo 'This is script2 (hello)'
printf '\$0 is "%s"\n' "\$0"
END_SCRIPT2
printf '$0 is "%s"\n' "$0"

Запуск этих выходов

This is script1 (hello)
$0 is "script1.sh"

... и script2.shсоздается:

#!/bin/sh
echo 'This is script2 (hello)'
printf '$0 is "%s"\n' "$0"

Обратите внимание, что внутренний здесь -документ создан с использованием <<END_SCRIPT2, а не <<-END_SCRIPT2. Любые начальные вкладки будут съедены, когда будет создан внешний здесь -документ (, как видно изscript1.sh).


Кроме того, вам не нужны два вызова waitв вашем коде, так как вы не запускаете никаких фоновых процессов.

Ваш код awkтакже неверен, так как условие f && /testphrase2/никогда не будет истинным. Если /testphrase/совпадает, то выполняется next, заставляя код продолжаться со следующей строки ввода. Если /testphrase/не соответствует, то и /testphrase2/не будет соответствовать.

Вместо этого вы можете попробовать:

awk '!f && /testphrase/  { f = 1; r = "" }
     f  && /testphrase2/ { f = 0 }
     f                   { r = (r == "" ? $0 : r RS $0) }
     END                 { print r }'
0
14.05.2020, 12:35
3 ответа

Вы можете использовать GNUawk:

LC_ALL=C find /db/users/logs/ -name '*.txt' -type f -exec gawk '{
  f = FILENAME; sub(".*/", "", f)
  print f" | "$0; nextfile}' {} +

илиperl:

LC_ALL=C find. -type f -name '*.txt' -exec perl -lne '
  print $ARGV =~ s:.*/::r. " | $_"; close ARGV' {} +

Или панцирь:

LC_ALL=C find /db/users/logs/ -type f -name '*.txt' -exec sh -c '
  for file do
    <"$file" IFS= read -r line || [ -n "$line" ] &&
      printf "%s\n"  "${file##*/} | $line"
  done' sh {} +

(что он не будет работать должным образом с большинством shреализаций, если первая строка содержит символы NUL; хотя этого не должно происходить для текстовых файлов ).

2
28.04.2021, 23:14

Метод -a)

find /db/users/logs -type f -name '*.txt' \
    ! -empty -printf '%f | ' \
    -exec head -n 1 \{\} \;

Метод ii )мы используем файл модуля perl ::Найдите, который инкапсулирует функциональность команды find

perl -MFile::Find -e '
  find( sub { my $fh;
     -f && ! -z && /\.txt$/ and 
     open($fh, "<", $_) and 
     print("$_ | ". <$fh>) },
   shift )
' /db/users/logs

Спасибо Stephane за указание на необходимость базовых имен. Вы можете сделать это также при условии, что в имени файла нет новых строк, и мы можем использовать двоеточие в качестве разделителя вместо вертикальной черты.

$ find /db/users/logs/  \
    -type f -name '*.txt' \
    -exec grep -Hm1 "^" {} + |
  sed 's|^/db/users/logs/\([^/]*/\)*||' 
0
28.04.2021, 23:14

Еще один вариант:

$ find /db/users/logs/ -type f -name "*.txt" -exec \
  sh -c 'printf "%s | %s\n" "$(basename $1)" "$(head -1 $1)"' shellproc {} \;

также будет обрабатывать пустые файлы и имена файлов с пробелом (s ). У пользователя MacOS может быть недоступно basename.

0
28.04.2021, 23:14

Теги

Похожие вопросы