как заменить строку в определенной позиции в CSV-файле, используя Bash - PullRequest
0 голосов
/ 03 ноября 2018

У меня есть несколько файлов .csv, и в каждом файле csv есть строки, которые выглядят так:

AA,1,CC,1,EE
AA,FF,6,7,8,9
BB,6,7,8,99,AA

Я читаю каждую строку каждого CSV-файла, а затем пытаюсь заменить 4-ю позицию каждой строки, начинающейся с AA, на "ZZ"

Ожидаемый результат

AA,1,CC,ZZ,EE
EE,FF,6,ZZ,8,9
BB,6,7,8,99,AA

Однако переменная "y" содержит 4-ю переменную "1" и "7" соответственно, но когда я использую команду sed, она заменяет первое вхождение "1" на "ZZ".

Как мне изменить мой код, чтобы заменить только 4-ю позицию каждой строки независимо от того, какое значение он содержит?

Мой код выглядит так

$ file = "имя файла, который содержит список всех файлов CSV"

for i in `cat file`
while IFS = read -r line;
do
if [[ $line == AA* ]] ; then
        y=$(echo "$line" | cut -d',' -f 4)
        sed -i "s/${y}/ZZ/" $i
fi
done < $i

Ответы [ 4 ]

0 голосов
/ 03 ноября 2018

Чтобы сделать это надежно, просто:

$ awk 'BEGIN{FS=OFS=","} $1=="AA"{$4="ZZ"} 1' csv
AA,1,CC,ZZ,EE
AA,FF,6,ZZ,8,9
BB,6,7,8,99,AA

Обратите внимание, что в приведенном выше примере выполняется сравнение буквенных строк и замена буквенных строк, поэтому в отличие от других опубликованных решений, он не потерпит неудачу, если целевая строка (AA в этом примере) содержит метасхемы регулярного выражения, такие как . или *, или если она может быть частью другой строки, такой как AAX, или если строка замены (ZZ в этом примере) содержит обратные ссылки, такие как & или \1.

Если вы хотите отобразить несколько строк за один проход:

$ awk 'BEGIN{FS=OFS=","; m["AA"]="ZZ"; m["BB"]="FOO"} $1 in m{$4=m[$1]} 1' csv
AA,1,CC,ZZ,EE
AA,FF,6,ZZ,8,9
BB,6,7,FOO,99,AA

и точно так же, как GNU sed имеет -i для редактирования на месте, GNU awk имеет -i inplace, так что вы можете отказаться от цикла оболочки и просто сделать:

awk -i inplace '
BEGIN { FS=OFS="," }
(NR==FNR) { ARGV[ARGC++]=$0 }
(NR!=FNR) && ($1=="AA") { $4="ZZ" }
{ print }
' file

и он будет работать со всеми файлами, указанными в file, за один вызов awk. «file» в последнем случае - это ваш файл, содержащий список других имен файлов CSV.

0 голосов
/ 03 ноября 2018

Использование sed:

sed -i 's/\(^AA,[^,]*,[^,]*,\)[^,]*/\1ZZ/' input_file
0 голосов
/ 03 ноября 2018

Используя sed, вы также можете указать, чтобы только 4-е поле файла значений, разделенных запятыми, было изменено на "ZZ" для строк, начинающихся "AA" с:

sed -i '/^AA/s/[^,][^,]*/ZZ/4' file

Пояснение

  • sed -i вызов sed для редактирования файла на месте;
  • общая форма /find/s/match/replace/occurrence; где
    • find - это /^AA/ строка, начинающаяся с "AA";
    • match [^,][^,]* символ не запятая, за которой следует любое количество не запятых;
    • replace /ZZ/4 4-е вхождение match с "ZZ".

Обратите внимание, что и awk, и sed обеспечивают хорошие решения в этом случае, поэтому посмотрите ответы по @ perreal и @ RavinderSingh13

Пример входного файла

$ cat file
AA,1,CC,1,EE
AA,FF,6,7,8,9
BB,6,7,8,99,AA

Пример использования / Вывод

( примечание: -i ниже не используется, поэтому изменения просто выводятся в stdout)

$ sed '/^AA/s/[^,][^,]*/ZZ/4' file
AA,1,CC,ZZ,EE
AA,FF,6,ZZ,8,9
BB,6,7,8,99,AA
0 голосов
/ 03 ноября 2018

РЕДАКТИРОВАТЬ1: Поскольку ОП изменило требование, добавьте немного следующего.

awk 'BEGIN{FS=OFS=","} /^AA/||/^BB/{$4="ZZ"} /^CC/||/^DD/{$5="NEW_VALUE"} 1'  Input_file > temp_file && mv temp_file Input_file

Не могли бы вы попробовать следующее.

awk -F, '/^AA/{$4="ZZ"} 1' OFS=,  Input_file > temp_file && mv temp_file Input_file

OR

awk 'BEGIN{FS=OFS=","} /^AA/{$4="ZZ"} 1'  Input_file > temp_file && mv temp_file Input_file

Объяснение: Добавление пояснения к вышеприведенному коду тоже сейчас.

awk '
BEGIN{              ##Starting BEGIN section of awk which will be executed before reading Input_file.
  FS=OFS=","        ##Setting field separator and output field separator as comma here for all lines of Input_file.
}                   ##Closing block for BEGIN section of this program.
/^AA/{              ##Checking condition if a line starts from string AA then do following.
  $4="ZZ"           ##Setting 4th field as ZZ string as per OP.
}                   ##Closing this condition block here.
1                   ##By mentioning 1 we are asking awk to print edited or non-edited line of Input_file.
'  Input_file       ##Mentioning Input_file name here.
...