Скрипт Bash для создания CSV-файла из текста

У меня есть текстовый файл с записями:

  Data1

  Data2

  ...

  Data50

Мне нужно создать файл .csv из приведенного выше текстового файла в следующем формате:

Type |  Count | Name

Def |    u1 |    Data1

Def |    u2  |     Data2

....  |  .....   |  ....

Def  |   u50   | Data50

мне нужен сценарий bash для создания файла .csv из текстового файла. Я новичок в написании сценариев оболочки! Я также прошел основы awk и sed. У меня есть смутное представление, например:

#!/bin/bash
type="Def"
x=1
count="u"
for F in ../test.txt
do
    {
       read \n
       echo "$type, $count$x, $..." >> ../test.csv
       x=x+1
    } < $F

done 

Я понимаю, что разделитель полей '\n'. Я как-то потерялся после этого.

Спасибо!


person Tamaghna Guha Thakurta    schedule 24.02.2015    source источник


Ответы (4)


сохраните это в файле, например. makecsv.rc:

#!/bin/sh
echo Type,Count,Name
x=0
for f in `cat`
do
   x=`expr $x + 1`
   echo Def,u$x,$f
done

затем запустите как:

cat  ../test.txt | ./makecsv.rc > ../test.csv

при необходимости вы делаете chmod +x makecsv.rc

Преимущество в том, что имена входных/выходных файлов не hardcoded

person Roland    schedule 24.02.2015
comment
Просто используйте "$1" и "$2", если вы не хотите жестко кодировать имена файлов. cat в обратных кавычках довольно ужасен. - person tripleee; 24.02.2015
comment
Это было очень полезно, так как мне нужно было выполнять его через определенные промежутки времени с разными файлами! Большое спасибо! - person Tamaghna Guha Thakurta; 24.02.2015
comment
@tripleee ты прав. Но в моем часовом поясе точно пора домой. Предложите, пожалуйста, как избежать ДВУХ cat: в скрипте и в командной строке. - person Roland; 24.02.2015
comment
Улучшение, устраняющее один cat: в скрипте используйте: for f in 'cat $1' и в командной строке: ./makecsv.rc < ../test.txt > ../test.csv Пожалуйста, используйте обратные кавычки вокруг кота в скрипте - person Roland; 24.02.2015
comment
Я добавил фрагмент к своему собственному ответу. Ваш можно было бы подвергнуть аналогичному рефакторингу, но тогда это был бы почти тот же ответ... - person tripleee; 24.02.2015
comment
@jm666 Правильно. Мое решение можно улучшить, заменив cat командой чтения строки. Однако в исходной задаче пробелы не упоминались. Кроме того, новое решение взорвется, если ввод содержит запятую, но это также можно исправить, добавив кавычки. Но тогда вам может понадобиться двойные кавычки во входных данных. Такие простые сценарии могут решать только простые проблемы. В противном случае вам лучше поискать подходящую библиотеку CSV. - person Roland; 27.02.2015
comment
@Roland, мой комментарий предназначен для других пользователей, которые могут использовать решение и у них может быть место. Помните, что ответы предназначены и для будущих пользователей. Я не хочу критиковать ваш ответ, но, честно говоря, я согласен с @tripleee - простая cat в обратной кавычке действительно ужасна и противоречит любой хорошей практике bash. Кроме того, в любой современной оболочке $(command) предпочтительнее использовать в качестве обратных кавычек. - person jm666; 27.02.2015
comment
@ jm666, спасибо, что указали на новый метод подстановки команд. Я выучил обратные кавычки около 20 лет назад, но мне не терпится освоить новые способы. Я понимаю, что $(...) в значительной степени эквивалентен обратным кавычкам, и что большинство людей предпочитают более новый метод. Но я не согласен с квалификацией как horror, просто мое мнение. - person Roland; 02.03.2015

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

#!/bin/bash
type="Def"
x=1
count="u"
while read value; do
   echo "$type, $count$x, $value"
   let x++
done <../test.txt > ../test.csv

Вы также можете использовать внешнюю утилиту для текущих чисел:

nl ../test.txt |
while read -r x value; do
    echo "$type,$count$x,$value"
done >../test.csv

Перенаправление вне цикла более эффективно, поскольку оболочке не нужно будет закрывать и снова открывать выходной файл.

Если вы хотите передать переменное имя файла в командной строке, просто замените жестко заданное ../test.txt на "$1". Вы могли бы аналогичным образом параметризовать имя выходного файла, но я бы просто удалил перенаправление вывода и предоставил бы вызывающей стороне решать, что делать с выводом сценария.

Если вам нужно прочитать несколько полей и разделить их запятыми, настройте IFS.

nl -s , ../test.txt |
while IFS=, read -r i first second rest; do
    printf "%i,%s,u%i,%s\n" $i "$first" "$second" "$rest"
done

(Рекомендуется использовать printf, а не echo, особенно если ваши выходные требования нетривиальны. Я не беру в кавычки $i специально, чтобы избавиться от любых начальных пробелов, добавленных nl перед номером строки. В противном случае вам следует всегда использовать двойные кавычки вокруг ваших переменных, если вы не специально требуют, чтобы оболочка выполняла токенизацию пробелов и расширение подстановочных знаков для значения.)

person tripleee    schedule 24.02.2015
comment
не забудьте строку заголовка csv! который по-прежнему легко добавить в свои решения - person Roland; 24.02.2015
comment
x=((x+1)) мне не помогло, очень жаль, мне не нравится expr - person Roland; 24.02.2015
comment
@Роланд использует x=$((x+1)) или let x++ - person jm666; 24.02.2015
comment
Большое спасибо, что нашли время. Вы правильно поняли, я хочу прочитать несколько строк в файле. Как я уже упоминал, я новичок в сценариях bash. Итак, я попробовал первый код, который вы предоставили, он выдал мне синтаксическую ошибку в «готово»! Можете ли вы дать мне некоторые подробности о внешней утилите. - person Tamaghna Guha Thakurta; 24.02.2015
comment
Дополнение должно работать с: x=$((x + 1)) но @jm666 вы были быстрее :-) Мне также нравится ваш 2-й вариант. - person Roland; 24.02.2015
comment
@TamaghnaGuhaThakurta просто попробуйте nl ../test.txt в командной строке и попробуйте man nl - person Roland; 24.02.2015
comment
@ jm666 jm666, поскольку OP, по-видимому, новичок в написании сценариев оболочки, не могли бы вы объяснить синтаксис вашего первого решения? Я бы тоже здесь поучился... - person Roland; 24.02.2015
comment
Спасибо за комментарии; исправлено. - person tripleee; 24.02.2015
comment
Есть веские причины опустить строку заголовка. Поскольку в примере OP его тоже не было, я его не добавлял; и рекомендовал бы против этого, если бы была строка заголовка. - person tripleee; 24.02.2015

Если у вас установлен Perl,

perl -lnE 'say qq{Def,u$.,"$_"}' < inputfile

сделает работу.

демо:

seq -f 'Some Data%g' 50 будет генерировать такие строки, как:

Some Data1
Some Data2
...
Some Data50

Итак

seq -f 'Some Data%g' 50 | perl -lnE 'say qq{Def,u$.,"$_"}'

отпечатки

Def,u1,"Some Data1"
Def,u2,"Some Data2"
...
Def,u49,"Some Data49"
Def,u50,"Some Data50"

Я процитировал последнее поле, потому что во вводе можно было получить , или пробелы.

на основе комментария @Roland, добавив строку заголовка:

cat data | (echo 'Type,Count,Name' ; perl -lnE 'say qq{Def,u$.,"$_"}')

or

perl -lnE 'BEGIN{say q{Type,Count,Name}}say qq{Def,u$.,"$_"}'

Если вам нужно решение bash, просто используйте:

cat -n filename | sed 's/ *\(.*\)\t\(.*\)/Def,u\1,"\2"/'

или сохранить

cat -n - | sed 's/ *\(.*\)\t\(.*\)/Def,u\1,"\2"/'

в какой-нибудь файл, например "makecsv", и используйте его как

./makecsv < data

Ps: хм... у @tripleee nl короче cat -n ;)

person jm666    schedule 24.02.2015
comment
сюда также легко добавить строку заголовка csv. Извините за это, но в настоящее время я много работаю с файлами csv, и это удивительно, как часто строка заголовка опускается без причины. Даже для файлов csv с чем-то вроде двух десятков столбцов... - person Roland; 24.02.2015

Вы делаете второе поле с:

x = `expr $x + 1`
$count$x

Весь сценарий становится:

#!/bin/sh

echo Type,Count,Name > test.csv
x=0
for f in `cat test.txt`
do
   x=`expr $x + 1`
   echo Def,u$x,$f >> test.csv
done

Удачи!

person Roland    schedule 24.02.2015
comment
Во! Спасибо! Я пропустил это. Спасибо за это! Но я застрял в том, как записать Data1, Data2... Data50 из .txt в новый файл .csv. - person Tamaghna Guha Thakurta; 24.02.2015
comment
Я тестировал это на своем настольном ПК с помощью Cygnus, выполняя stackoverflow на ноутбуке, поэтому мне пришлось перепечатать его. Я сделал опечатку? - person Roland; 24.02.2015
comment
ты был прав. Я исправил, используя cat, с обратными кавычками. Извините, что отвечаю слишком быстро :-) - person Roland; 24.02.2015
comment
Я попробовал ваш код, но кажется, что цикл for не работает с командой cat - person Tamaghna Guha Thakurta; 24.02.2015
comment
@TamaghnaGuhaThakurta не забывайте о обратных кавычках вокруг cat. Я проверил это. И я нахожусь в другом каталоге, поэтому я опустил .. для имен файлов. - person Roland; 24.02.2015
comment
Просто чтобы убедиться, что я скопировал и вставил весь код из среды Cygnus (unix под windows), где я это тестировал. - person Roland; 24.02.2015
comment
@TamaghnaGuhaThakurta Добро пожаловать! Пожалуйста, не забудьте отметить выбранное решение, нажав на значок V. Это поможет другим с той же проблемой - person Roland; 24.02.2015
comment
@Roland, у меня есть более одного столбца, как получить один столбец с помощью $f. В вашем ответе $f возвращает полную строку - person pbms; 23.03.2017
comment
@Pbms read может считывать несколько переменных, просто перечислите нужные поля. while read -r first second rest; do ... done <file, что также устраняет надоедливые проблемы с for за ссылкой в ​​предыдущем комментарии. Если ваши поля не разделены пробелами, вы можете использовать IFS=,, например, для разделения запятой. Все это очень хорошо освещено в различных FAQ. - person tripleee; 23.03.2017