Заменить строку в нескольких файлах, используя построчные строки из одного файла

Когда я создал список файлов из вашего примера, мой lsсортирует их таким образом:

$ ls -1
myFile.c
mySecondFile.c
t-1-myFirstTest.c
t-21-tset241.c
t-3-test1234.c
t-42-my_second_test.c

В результате нижеприведенная функция bash выводит символы новой строки и номера для файлов в том же порядке.

I want to delete everything of this text except the newlines and the numbers between t- and the second -

Я интерпретировал это как означающее, что имена файлов, которые не соответствуют t-, должны быть «удалены, за исключением новой строки», что означает, что :выводит пустую строку для этих имен файлов, но в противном случае выводит числа между тире.

lsnums ()
{
    for f in *
    do
        if [[ "$f" =~ t-([[:digit:]]+)- ]]; then
            printf '%s\n' "${BASH_REMATCH[1]}"
        else
            echo
        fi
    done
}

В результате получается:

$ lsnums


1
21
3
42

... где две пустые строки соответствуют файлам первого типа, начинающимся с myвместо t-.

1
04.08.2020, 04:05
5 ответов

препараты

Только одна строка в каждом файле.

$ grep --. ?.txt
a.txt:a#P#b
b.txt:c#P#d
c.txt:e#P#f
d.txt:g#P#h
$ cat input
opq  111
rst  222
uvw  333
xyz  444

решение

Вызов цикла оболочки sedдля каждого файла:

for file in ?.txt; do
    read -r dummy new_string rest
    sed -- "s/#P#/$new_string/g" "$file"
done <input

a111b
c222d
e333f
g444h

Измените это на sed -iс GNU sedили совместимым или sed -i ''с FreeBSD sedили совместимым, если вы удовлетворены результатом изменения файлов.

Вышеприведенное предполагает, что строки inputне содержат символов &, /и \. Если они могут, вам придется сначала избегать тех, у кого есть обратная косая черта.

2
18.03.2021, 23:15
#!/bin/sh
mv eg.txt eg.input
awk 'NR==FNR{a[++i]=$2;next}{sub("#P#",a[++j]);print>(FILENAME".new")}' eg.input./*.txt &&
for f in *.txt; do mv "$f.new" "$f"; done
mv eg.input eg.txt

eg.txtпереименовывается в eg.inputи потом обратно так, что *.txtв строке awk расширяется только на те файлы, которые должны быть изменены.

NR==FNR{    #For the first file, eg.input
  a[++i]=$2   #Put the second field in the array `a`
  next        #Skip the rest of the code
}
{                        #For the other files
  sub("#P#",a[++j])        #Make the substitution
  print>(FILENAME".new")   #Print to the line to `FILENAME`.new
}

Затем в цикле for содержимое старых файлов *.txtперезаписывается содержимым файлов *.new. Вы можете захотеть подавить цикл for, пока не убедитесь, что файлы *.newверны.


Некоторые реализации awk не поддерживают много открытых файлов (GNU awk поддерживает ). Если ваш awk завершается с ошибкой «слишком много открытых файлов», используйте этот вариант,

awk 'NR==FNR{a[++i]=$2;next}FNR==1{close(fn);fn=FILENAME".new"}{sub("#P#",a[++j]);print>fn}'
2
18.03.2021, 23:15

Поскольку вы уже находитесь на zsh, и я предполагаю, что вы используете версию GNU sed, мы можем сделать это, как показано в двухэтапном процессе.

setopt extended_glob

sed -Ei -e '/#P#/R eg.txt'./(^eg).txt

sed -Ei -e '/#P#/N;s/#P#(.*)\n.*\s(.*)/\2\1/'./(^eg).txt

Краткое пояснение

  • Включите расширенную подстановку, чтобы мы могли отфильтровать определенный файл, например.txt, из командной строки sed.

  • Поместите соответствующую строку из eg.txt после строки, содержащей #P #, с помощью команды R. Прочтите об этой конкретной команде GNU в руководстве для получения дополнительной информации.

  • Здесь мы объединяем две строки и выполняем операцию вырезания и вставки, чтобы получить желаемый результат.

Файлы были отредактированы на месте (, за исключением, например,.txt)

1
18.03.2021, 23:15

Использование GNU awk для редактирования «на месте» иARGIND:

awk -i inplace '
    NR == FNR { map[NR]=$2 }
    NR != FNR { sub(/#P#/,map[ARGIND]) }
1' eg.txt ?.txt

Вышеприведенное предполагает, что замещающий текст из eg.txtне содержит пробелов или &с.

3
18.03.2021, 23:15
eg.txt

opq  111
rst  222
uvw  333
xyz  444

a.txt

a#P#b
12345
apple

b.txt

c#P#d
56788

команда

j=1;for i in "a.txt" "b.txt" ; do  b=`sed -n ''$j'p' eg.txt| awk '{print $2}'`;sed "s/#P#/$b/g" $i;echo "=================";j=$(($j+1)); done


output

below are the output of a.txt
a111b
12345
apple
=================
below are the output of b.txt
c222d
56788
=================
-1
18.03.2021, 23:15

Теги

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