У меня есть много файлов .txt, которые выглядят так:
файл1.txt
header
1_fff_aaa 1_rrr_aaa 1_ggg_aaa ...
файл2.txt
header
1_ttt_aaa 1_iii_aaa 1_lll_aaa ...
Я хотел бы удалить заголовок и разделить строку второй строки на несколько строк после пробела и использовать шаблон между символом _:
Выход:
file1_v1.txt
fff
rrr
ggg
file2_v1.txt
ttt
iii
lll
Я хотел бы использовать команды Unix, такие как sed





Что-то вроде того:
Программа: split.awk
NR == 1 {
# ignore first header line
next
}
{
i=1
while (i <= NF) {
gsub(/^[^_]*_/, "", $i)
gsub(/_[^_]*$/, "", $i)
print $i
i++
}
}
Выполнено так:
awk -f split.awk file1.txt > file1_v1.txt
Чтобы выполнить его для многих файлов:
for f in file*.txt; do echo "$f"; awk -f split.awk "$f" > "${f%.txt}_v1.txt" ; done
Вы также можете использовать sed и tr:
sed -n '2,$p' file1.txt | tr " " "\n" | sed 's/^[^_]*_\(.*\)_[^_]*$/\1/'
Это может сработать для вас (GNU sed):
sed -i '1d;s/\s\+/\n/g;s/^[^_]*_//mg;s/_.*//mg' file1 file2 file3 ...
Используйте параметр командной строки -i для замены inline.
Удалите первую строку каждого файла (удалите заголовок).
Замените пробелы на новые строки. Это преобразует каждый токен в отдельную строку.
Удалите первую часть строки до первого _ включительно для всех строк в пространстве шаблона.
Удалить от первого _ до конца строки, оставив результат.
Н.Б. Опцию -i можно заменить опцией -s, если пользователю требуется вывод на стандартный вывод только одного или нескольких файлов. Также обратите внимание на флаг m в последних двух командах замены, который меняет обычную замену, чтобы использовать многострочные шаблоны.
Чтобы изменить имена выходных файлов, используйте GNU Parallel:
parallel --plus "sed '1d;y/ /\n/;s/^[^_]*_//mg;s/_.*//mg' {} > {.}_v1.{+.}" ::: file1.txt file2.txt ...
Обычно я бы не стал отвечать на вопрос, на который ФП не предпринял никаких попыток решить свою проблему самостоятельно, но поскольку уже есть несколько ответов...
Используя любой awk:
$ cat tst.awk
BEGIN { FS = "_" }
FNR == 1 {
close(out)
out = FILENAME
sub(/\.txt$/,"_v1&",out)
next
}
{
for ( i=2; i<=NF; i+=2 ) {
print $i > out
}
}
$ awk -f tst.awk file{1,2}.txt
$ head file{1,2}_v1.txt
==> file1_v1.txt <==
fff
rrr
ggg
==> file2_v1.txt <==
ttt
iii
lll
NR == 1 { next } { foo }=NR > 1 { foo }. Вам не нуженgsub(), когда регулярное выражение привязано к началу или концу строки, достаточноsub(), поскольку оно может соответствовать только один раз. Однако вы могли бы использоватьgsub(/^[^_]*_|_[^_]*$/,"",$i)вместо двух отдельныхsub()в POSIX awk.i=1; while (i <= NF) { foo; i++ }чаще пишетсяfor (i=1; i<=NF; i++) { foo }.