Обработка текста - соединяйте каждые две строки запятыми


35

У меня есть более 1000 строк в файле. Файл начинается следующим образом (добавляются номера строк):

Station Name
Station Code
A N DEV NAGAR
ACND
ABHAIPUR
AHA
ABOHAR
ABS
ABU ROAD
ABR

Мне нужно преобразовать это в файл с разделенными запятыми записями, соединяя каждые две строки. Окончательные данные должны выглядеть

Station Name,Station Code
A N DEV NAGAR,ACND
ABHAIPUR,AHA
ABOHAR,ABS
ABU ROAD,ABR
...

Я пытался написать сценарий оболочки, а затем echoзапятую между ними. Но я думаю, что более простая эффективная однострочная работа могла бы быть здесь, в sed/ awk.

Любые идеи?


@ l0b0 Вы отредактировали замечание ОП о том, что номера строк "только там для объяснения" ...
Джейсонвриан

@jasonwryan Извините, я думал, что строки были там для объяснения. Ошибка разбора в строке 0.
10

Ответы:


39

Просто используйте cat(если вы любите кошек ;-)) и paste:

cat file.in | paste -d, - - > file.out

Объяснение: pasteчитает из ряда файлов и вставляет вместе соответствующие строки (строка 1 из первого файла со строкой 1 из второго файла и т. Д.):

paste file1 file2 ...

Вместо имени файла мы можем использовать -(тире). pasteберет первую строку из file1 (который является stdin). Затем он хочет прочитать первую строку из файла file2 (который также является stdin). Однако, так как первая строка stdin уже была прочитана и обработана, то, что теперь ожидает во входном потоке, является второй строкой stdin, которая pasteуспешно приклеивается к первой. -dОпция устанавливает разделитель быть запятая , а не вкладка.

Или сделать

cat file.in | sed "N;s/\n/,/" > file.out

PS Да, выше можно упростить до

< file.in sed "N;s/\n/,/" > file.out

или

< file.in paste -d, - - > file.out

который имеет преимущество не использовать cat.

Тем не менее, я не использовал эту идиому нарочно , для наглядности - это менее многословным , и я , как cat(Кошкам NICE). Поэтому, пожалуйста, не редактируйте.

В качестве альтернативы, если вы предпочитаете вставлять кошки (команда вставки объединяет файлы по горизонтали, а кошка объединяет их по вертикали), вы можете использовать:

paste file.in | paste -d, - -

Просто чтобы упомянуть это снова. Номера строк не являются частью файла :)
MTK

Команда paste отлично работает, не могли бы вы дать немного больше объяснений по этому поводу. Дефисы ???
MTK

2
Дефисы означают «читать со стандартного ввода». Если один и тот же источник ввода повторяется, Paste знает, что нужно читать его несколько раз за строку вывода.
dubiousjim

@sch: Cool Edit, я не буду трогать :-)
января

1
Что касается вашего catаргумента. Не sed "N;s/\n/,/" file.in > file.outработает?
Бернхард

8

В случае, если кто-то приземлится здесь хочет объединить все линии в один лайнер CSV, попробуйте

cat file | tr '\n' ','

3
sed 'N;s/\n/,/' file

Используя sed, объедините (N) каждые 2 строки и замените символ новой строки (\ n) на «,».


3
paste -sd ',\n' file.in > file.out

Также обратите внимание, что, поскольку мы просто заменяем один символ другим (каждый новый перевод строки запятой), мы можем работать с входным файлом на месте:

paste -sd ',\n' file.in 1<> file.in

(но учтите, что он может не работать на не-Unix-системах, которые имеют терминаторы CRLF (например, Microsoft), которые некоторые эмулированные POSIX pasteмогут обрабатывать не-Unix-способом)


Что это 1делает здесь 1<>? это опечатка?
α'sнιη

@ αғsнιη, посмотри на это
iruvar

@iruvar спасибо
αғsнιη

2

Вот одна строка (хотя, возможно, миллионы команд-запуска-er), использующая чистый Bash:

(IFS=; while read -r name; do read -r code; printf '%s\n" "$name,$code"; done < file.in) > file.out

Я использую подоболочку (парантез), чтобы мне не пришлось хранить и восстанавливать IFS. Что в противном случае следует сделать, чтобы не испортить среду пользователя, если исходный код получен. Альтернативой было бы передать , что новый стандарт IFS только readкак в IFS= read -r name,IFS= read -r code .

Тот факт, что все команды в цикле встроены в оболочку, делает ее производительность приемлемой и даже быстрее, чем другие решения для небольших файлов. Но многие люди считают это плохой практикой, и нужно быть осторожным, обобщая это на что-то еще.


в общем, yay для использования подоболочек для локализации изменений среды. Но в этом случае это не нужно: вместо этого вы можете сделать while IFS='\n' read -r name; do IFS='\n' read -r code ... done < file.in, что часто встречается в сценариях оболочки. -rФлаг readозначает «интерпретировать символ„\“ , за которой следует символ„п“в потоке стандартного ввода в виде двух символов, а не как символ новой строки.» Возможно, было бы более эстетично создавать подоболочку, чем вы, чем повторять IFS='\n'.
dubiousjim

@dubiousjim: -rулучшено техническое решение. Большой! Я не фанат идеи прохождения измененного IFSдважды. Если бы я использовал одно чтение, супер приятно, но не дважды. Конечно, это вопрос мнения . Я бы сказал, что использование подоболочки немного превышает общие знания Bash, поэтому многим людям будет сложно понять его назначение. Это плохо.
удалено

2

Для полного набора ответов возможное awkрешение может быть:

awk 'NR%2==1 {printf $0","} NR%2==0 { print $0}' *file*

@ downvoter: Что не так с моим ответом, чтобы заслужить понижение? как это может быть улучшено?
Бернхард

Может потому что ленивый printf? Сбой в редком случае, когда имя станции содержит спецификатор формата. (См. Пример pastebin.com/wgxFttrJ .) Но это всего лишь предположение, отрицательное мнение не от меня.
Манатворк

1

Седой старый каштан awkидиомы

awk '{ORS=NR%2?",":"\n";print}' file
Station Name,Station Code
A N DEV NAGAR,ACND
ABHAIPUR,AHA
ABOHAR,ABS
ABU ROAD,ABR

awk '{ORS=NR%2?",":"\n"};1'короче и более идиома
cuonglm

@cuonglm, я сомневаюсь в этом. В этом случае это все еще однострочник, несмотря на то, printчто цель ясна. 1так же ясно для старых awkрук, как я, но я предпочитаюprint
iruvar

Я обнаружил, что это было первое простое решение, которое можно было легко настроить более чем на две строки. Некоторое время я боролся с sedпоиском, но awkоблегчил объединение каждых 4 строк. Спас мне поездку на $EDITOR!
opello


0

Например:

seq 0 70 | xargs -L 2 | sed 's/ /,/g'

Вывод: (примечание: xargs -L number_of_columnsпрекрасно работает с любым количеством столбцов, а не только с двумя строками)

0,1
2,3
4,5
6,7
8,9
10,11
12,13
14,15
16,17
18,19
20,21
22,23
24,25
26,27
28,29
30,31
32,33
34,35
36,37
38,39
40,41
42,43
44,45
46,47
48,49
50,51
52,53
54,55
56,57
58,59
60,61
62,63
64,65
66,67
68,69
70

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.