Рассмотрим следующий пример скрипта (все отступы являются буквальными вкладками):
#!/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 }'
Вы можете использовать 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; хотя этого не должно происходить для текстовых файлов ).
Метод -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/\([^/]*/\)*||'
Еще один вариант:
$ find /db/users/logs/ -type f -name "*.txt" -exec \
sh -c 'printf "%s | %s\n" "$(basename $1)" "$(head -1 $1)"' shellproc {} \;
также будет обрабатывать пустые файлы и имена файлов с пробелом (s ). У пользователя MacOS может быть недоступно basename
.