Если вывод может быть смешанным (не сохранять исходный порядок строк), вы можете использовать join
join -o 2.1,1.2 -2 2 -t':' \
<(sed 's/^/ /;s/ =/:/' File1 |sort -t: -k1,1)\
<(sort -t: -k2 File2)
Для оболочки со встроенной командой read
, которая может обрабатывать CSV, вы можете использовать ksh93
вместо bash
:
$ while IFS='|' read -rS a b c; do printf '%s\n' "$b"; done < file
111
222
333
Чтобы преобразовать этот формат во что-нибудь что bash
read
может обработать, вы можете сделать:
< file ksh93 -c 'while IFS="|" read -rSA a; do
printf "%s|" "${a[@]//[\|]/\\\0}"
printf "\0"
done' |
bash -c 'while IFS="|" read -d "" a b c; do
printf "%s\n" "$b"
done'
Вам действительно стоит использовать правильный анализатор CSV. Например, используя тот, который поставляется с ruby:
ruby -rcsv -e 'CSV.foreach("file", :col_sep => "|") {|row| p row; puts row[1]}'
мы получаем
["aaa", "111", "!!!", ""]
111
["bbb", "222", "@@\n@", ""]
222
["ccc", "333", "###", ""]
333
Вы можете видеть для 2-й строки, есть встроенная новая строка. Удалите p row
, чтобы избавиться от этих «отладочных» строк.
ОК, лучшим решением для меня (но, думаю, это дело вкуса) было использование fgetcsv
от PHP, поскольку у меня уже есть PHP на этом сервере. Жаль, что команда bash read
не обрабатывает новые строки так же хорошо, как функция PHP. Она автоматически распознает дополнительные разделители (например, "'s").
Образец:
<?php
$row = 1;
if (($handle = fopen("test.csv", "r")) !== FALSE) {
while (($data = fgetcsv($handle, 10000, "|")) !== FALSE) {
$num = count($data);
echo "$num fields in line $row:\n";
$row++;
for ($c=0; $c < $num; $c++) {
echo $c + 1 . ": " . $data[$c] . "\n";
}
}
fclose($handle);
}
?>
Вывод (например, в моем исходном вопросе):
4 fields in line 1:
1: aaa
2: 111
3: !!!
4:
4 fields in line 2:
1: bbb
2: 222
3: @@
@
4:
4 fields in line 3:
1: ccc
2: 333
3: ###
4: