С помощью gnu sed
:
sed -Es '/pattern1|pattern2|pattern3/{
s/.*:[[:blank:]]*//;H}
$!d;x;/^\n$/d;s/\n(.*)/\1,/;s/\n/,/g' folder/*.txt > list.txt
где list.txt
содержимое будет примерно таким:
file1match1,file1match2,
file2match1,
file4match1,file4match2,file4match3,
так file3
отсутствует в выводе, поскольку не было строки, соответствующей pattern*.
Как это работает: обрабатывает каждый файл -s
отдельно, удаляя (через s/.*:[[:blank:]]*//
) ненужную часть в строках, соответствующих pattern* и добавляя результат в H
old буфер. Он удаляет каждую строку, кроме la$
t, когда он ex
меняет буферы. Если в пространстве шаблона есть только \n
ewline, это означает, что ни одна строка в этом файле не соответствует pattern*, поэтому он удаляет пространство шаблона. Иначе он удаляет ведущую \n
строку, заменяет оставшиеся запятыми и добавляет последующую запятую.
С другими sed
ами вам придется выполнить цикл:
for file in folder/*.txt do
sed '/pattern1\|pattern2\|pattern3/{
s/.*:[[:blank:]]*//
H
}
$!d
x
/^\n$/d
s/\n\(.*\)/\1,/
s/\n/,/g' "$file"
done > list.txt
Есть несколько вариантов (сначала bash, ниже POSIX).
Код внутри каждой функции можно легко использовать снаружи.
#!/bin/bash
nl=$'\n'
aregex (){ [[ $a =~ $nl ]]; }
apattern (){ [[ $a == *$nl* ]]; }
acut (){ [[ $a != "${a%%"$nl"*}" ]]; }
areplace (){ [[ $a != "${a//"$nl"/}" ]]; }
acase (){ case $a in (*$nl*) true;; (*) false;; esac; }
aifs ()( IFS="$nl"
set -f; set -- x${a}x;
(( $# > 1 ));
)
aread (){ IFS="$nl" read -rd '' -a b <<<"x${a}x";
(( "${#b[@]}" > 1 )); }
Каждая функция - это опция. Каждая функция завершается с возвращаемым значением 0, если есть только одна строка, и возвращаемым значением 1, если переменная $ a
имеет более одной новой строки (более одной $ '\ n '
).
После выполнения функции будет напечатана требуемая строка:
out=''; "$function" && out="more than "
printf "%9s = %sone line\n" "$1" "$out"
Выполнение всех параметров:
a='ab'"$nl"'cd' ; alltests
a='ab cd' ; alltests
Выдает следующий результат:
aregex = more than one line
apattern = more than one line
acut = more than one line
areplace = more than one line
acase = more than one line
aifs = more than one line
aread = more than one line
aregex = one line
apattern = one line
acut = one line
areplace = one line
acase = one line
aifs = one line
aread = one line
Следующие параметры не работают в POSIX по нескольким причинам:
= ~
. ==
. $ {/ /}
. Четыре можно безопасно перевести в POSIX:
set -f
. -d
или -a
, но его можно адаптировать. #!/bin/dash
nl='
'
posixcut (){ [ "${a}" != "${a%%"$nl"*}" ] ; }
posixcase(){ case $a in (*$nl*) true;; (*) false;; esac; }
posixifs ()( IFS="$nl";
set -f; set -- x${a}x;
[ "$#" -gt 1 ];
)
posixread(){ local b=0;
while IFS=$nl read -r _; do
b=$((b+1));
[ $b -gt 1 ] && break;
done <<-_EOT_
x${a}x
_EOT_
[ $b -gt 1 ];
}
Примечание : Да, local
не является строго POSIX, но довольно хорошо поддерживается .
Локальный объект для b
можно безопасно удалить (проверьте глобальное использование $ b
).
Если вам нужен код для исходной оболочки Bourne 1977 года, используйте это:
posixcase() { case $a in *$nl*) true;; *) false;; esac; }
posixifs () ( IFS="$nl"
set -f; set -- x${a}x;
[ "$#" -gt 1 ];
)
bourneread()( b=0 ### A helper function.
while :; do
if IFS=$nl read c && [ $b -le 1 ]; then
b=`expr $b + 1`
else
echo "$b"
break
fi
done <<-_EOT_
x${a}x
_EOT_
)
posixread (){ [ `bourneread` -gt 1 ]; }
Расширение $ {a %%}
, используемое в posixcut, не будет работать в Bourne.
Posixifs Чтобы работать в Борне, его нужно разделить на две части.
Параметр чтения требует изменений мэра, но работает правильно.
Его функция - posixread, bourneread - обязательная вспомогательная функция.
Весь код Bourne был протестирован на предмет правильной работы с семейной оболочкой .
Вот способ, который должен работать со всем синтаксисом Борна / оболочками POSIX и использует только встроенные функции:
if (set -f ; IFS=$'\n'; set -- x${myvar}x ; [ $# = 1 ]) ; then
echo "Your variable has only one line, proceeding"
else
echo "Error condition, variable must have exactly one line"
fi
Если ваша оболочка не поддерживает IFS = $ '\ n'
(например, тире
0.5.7), вы можете вместо этого использовать:
IFS="
"
Способ POSIX:
NL='
'
case $myvar in
*"$NL"*) echo more than one line ;;
*) echo one line ;;
esac
Это также работает в оболочках, подобных Bourne до POSIX.
Следующие фрагменты работают в bash
(с параметром -posix
и без него):
#!/bin/bash
#!/bin/bash -posix
version_1 () { [[ "$myvar" = *$'\n'* ]]; }
version_2 () {
local newlines="${myvar//[^$'\n']/}"
[[ "${#newlines}" -eq 1 ]]
}
for test in version_1 version_2; do
if $test; then echo many lines; else echo one line; fi
done
Я бы рекомендовал использовать расширение параметра Bash следующим образом:
n="${myvar//[^\n]}"; if [ ${#n} -eq 1 ]; then
echo "Your variable has only one line, proceeding"
else
echo "Error condition, variable must have only one line"
fi
Тестовые образцы:
myvar="xxx"
myvar="xx\nxx"
myvar="xx\nx\nx"