Если у меня есть файл csv, есть ли быстрый способ bash распечатать содержимое только одного столбца? Можно с уверенностью предположить, что каждая строка имеет одинаковое количество столбцов, но содержимое каждого столбца будет иметь разную длину.
Если у меня есть файл csv, есть ли быстрый способ bash распечатать содержимое только одного столбца? Можно с уверенностью предположить, что каждая строка имеет одинаковое количество столбцов, но содержимое каждого столбца будет иметь разную длину.
Ответы:
Вы можете использовать для этого awk. Измените "$ 2" на n-й столбец, который хотите.
awk -F "\"*,\"*" '{print $2}' textfile.csv
gawk -F"|" "{print $13}" files*.csv
...,"string,string",...
"
а последний заканчиваться"
awk -F "\"*;\"*" '{print $2}' textfile.csv
да. cat mycsv.csv | cut -d ',' -f3
напечатает 3-й столбец.
awk
Самый простой способ сделать это - просто использовать csvtool . У меня были и другие варианты использования csvtool, и он может соответствующим образом обрабатывать кавычки или разделители, если они появляются в самих данных столбца.
csvtool format '%(2)\n' input.csv
Замена 2 на номер столбца эффективно извлечет данные столбца, которые вы ищете.
cat input.csv | csvtool formath '%(2)\n' -
Примечание. Я знаю, что здесь cat бесполезен, но использовать его для любой команды, которая обычно экспортирует csv.
format '%(2)\n'
команда не может определить, где заканчивается одно поле. (csvtool 1.4.2)
csvtool
похоже, требуют использования -
в качестве входного имени файла для чтения из stdin.
csvtool format '%(1),%(10)\n' - < in.csv > out.csv
Приземлился сюда, чтобы извлечь из файла, разделенного табуляцией. Подумал добавить.
cat textfile.tsv | cut -f2 -s
Где -f2
извлекает 2, ненулевой индексированный столбец или второй столбец.
cat
этом нет необходимости:< textfile.tsv cut -f2 -s
Многие ответы на эти вопросы прекрасны, а некоторые даже заглянули в крайние случаи. Я хотел бы добавить простой ответ, который можно использовать ежедневно ... когда вы в основном попадаете в эти угловые случаи (например, экранирование запятых или запятых в кавычках и т. Д.).
FS (разделитель полей) - это переменная, значение которой равно пробелу. Таким образом, awk по умолчанию разделяет любую строку на пробел.
Таким образом, используя BEGIN (Выполнить перед вводом), мы можем установить в этом поле все, что захотим ...
awk 'BEGIN {FS = ","}; {print $3}'
Приведенный выше код напечатает 3-й столбец в файле csv.
Другие ответы работают хорошо, но поскольку вы запросили решение, используя только оболочку bash, вы можете сделать это:
AirBoxOmega:~ d$ cat > file #First we'll create a basic CSV
a,b,c,d,e,f,g,h,i,k
1,2,3,4,5,6,7,8,9,10
a,b,c,d,e,f,g,h,i,k
1,2,3,4,5,6,7,8,9,10
a,b,c,d,e,f,g,h,i,k
1,2,3,4,5,6,7,8,9,10
a,b,c,d,e,f,g,h,i,k
1,2,3,4,5,6,7,8,9,10
a,b,c,d,e,f,g,h,i,k
1,2,3,4,5,6,7,8,9,10
a,b,c,d,e,f,g,h,i,k
1,2,3,4,5,6,7,8,9,10
А затем вы можете вытащить столбцы (первые в этом примере) следующим образом:
AirBoxOmega:~ d$ while IFS=, read -a csv_line;do echo "${csv_line[0]}";done < file
a
1
a
1
a
1
a
1
a
1
a
1
Итак, здесь происходит несколько вещей:
while IFS=,
- это говорит об использовании запятой в качестве IFS (внутреннего разделителя полей), который оболочка использует, чтобы знать, что разделяет поля (блоки текста). Так сказать IFS = - это все равно, что сказать, что "a, b" то же самое, что "a b" было бы, если бы IFS = "" (что и есть по умолчанию).
read -a csv_line;
- это говорит читать в каждой строке, по одной, и создавать массив, каждый элемент которого называется "csv_line", и отправлять его в раздел "do" нашего цикла while.
do echo "${csv_line[0]}";done < file
- теперь мы находимся в фазе «do», и мы говорим, что эхо 0-й элемент массива «csv_line». Это действие повторяется в каждой строке файла. Эта < file
часть просто сообщает циклу while, откуда читать. ПРИМЕЧАНИЕ: помните, что в bash массивы индексируются 0, поэтому первый столбец - это 0-й элемент.
Итак, вы получили столбец из CSV в оболочке. Другие решения, вероятно, более практичны, но это чистый bash.
Вы можете использовать GNU Awk, см. Эту статью руководства пользователя . В качестве улучшения решения, представленного в статье (июнь 2015 г.), следующая команда gawk позволяет использовать двойные кавычки внутри полей с двойными кавычками; там двойная кавычка помечается двумя последовательными двойными кавычками (""). Кроме того, это позволяет пустые поля, но даже это не может обрабатывать многострочные поля . В следующем примере печатается 3-й столбец ( c=3
переход) textfile.csv:
#!/bin/bash
gawk -- '
BEGIN{
FPAT="([^,\"]*)|(\"((\"\")*[^\"]*)*\")"
}
{
if (substr($c, 1, 1) == "\"") {
$c = substr($c, 2, length($c) - 2) # Get the text within the two quotes
gsub("\"\"", "\"", $c) # Normalize double quotes
}
print $c
}
' c=3 < <(dos2unix <textfile.csv)
Обратите внимание на использование dos2unix
для преобразования возможных разрывов строк в стиле DOS (CRLF, т.е. «\ r \ n») и кодировки UTF-16 (с отметкой порядка байтов) в «\ n» и UTF-8 (без отметки порядка байтов) соответственно. Стандартные файлы CSV используют CRLF как разрыв строки, см. Википедию .
Если ввод может содержать многострочные поля, вы можете использовать следующий скрипт. Обратите внимание на использование специальной строки для разделения записей в выводе (поскольку новая строка разделителя по умолчанию может встречаться внутри записи). Опять же, в следующем примере печатается третий столбец ( c=3
переход) textfile.csv:
#!/bin/bash
gawk -- '
BEGIN{
RS="\0" # Read the whole input file as one record;
# assume there is no null character in input.
FS="" # Suppose this setting eases internal splitting work.
ORS="\n####\n" # Use a special output separator to show borders of a record.
}
{
nof=patsplit($0, a, /([^,"\n]*)|("(("")*[^"]*)*")/, seps)
field=0;
for (i=1; i<=nof; i++){
field++
if (field==c) {
if (substr(a[i], 1, 1) == "\"") {
a[i] = substr(a[i], 2, length(a[i]) - 2) # Get the text within
# the two quotes.
gsub(/""/, "\"", a[i]) # Normalize double quotes.
}
print a[i]
}
if (seps[i]!=",") field=0
}
}
' c=3 < <(dos2unix <textfile.csv)
Есть другой подход к проблеме. csvquote может выводить содержимое CSV-файла, измененного таким образом, что специальные символы в поле преобразуются, чтобы можно было использовать обычные инструменты обработки текста Unix для выбора определенного столбца. Например, следующий код выводит третий столбец:
csvquote textfile.csv | cut -d ',' -f 3 | csvquote -u
csvquote
может использоваться для обработки произвольных больших файлов.
Вот пример файла csv с 2 столбцами
myTooth.csv
Date,Tooth
2017-01-25,wisdom
2017-02-19,canine
2017-02-24,canine
2017-02-28,wisdom
Чтобы получить первый столбец, используйте:
cut -d, -f1 myTooth.csv
f обозначает поле, а d обозначает разделитель
Выполнение указанной выше команды приведет к следующему результату.
Вывод
Date
2017-01-25
2017-02-19
2017-02-24
2017-02-28
Чтобы получить только 2-й столбец:
cut -d, -f2 myTooth.csv
А вот и результат Output
Tooth
wisdom
canine
canine
wisdom
incisor
Другой вариант использования:
Ваш входной файл csv содержит 10 столбцов, и вам нужны столбцы со 2 по 5 и столбцы 8, используя запятую в качестве разделителя ".
cut использует -f (что означает «поля») для указания столбцов и -d (что означает «разделитель»), чтобы указать разделитель. Вам необходимо указать последнее, потому что в некоторых файлах могут использоваться пробелы, табуляции или двоеточия для разделения столбцов.
cut -f 2-5,8 -d , myvalues.csv
cut - это командная утилита, и вот еще несколько примеров:
SYNOPSIS
cut -b list [-n] [file ...]
cut -c list [file ...]
cut -f list [-d delim] [-s] [file ...]
Мне нужен был правильный анализ CSV, а не cut
/ awk
и молитва. Я пробую это на Mac без csvtool
, но Mac действительно поставляется с рубином, поэтому вы можете:
echo "require 'csv'; CSV.read('new.csv').each {|data| puts data[34]}" | ruby
Сначала мы создадим базовый CSV
[dumb@one pts]$ cat > file
a,b,c,d,e,f,g,h,i,k
1,2,3,4,5,6,7,8,9,10
a,b,c,d,e,f,g,h,i,k
1,2,3,4,5,6,7,8,9,10
Тогда получаем 1-й столбец
[dumb@one pts]$ awk -F , '{print $1}' file
a
1
a
1
Я думаю, что проще всего использовать csvkit :
Получает 2-й столбец:
csvcut -c 2 file.csv
Однако есть также csvtool и, вероятно, ряд других инструментов csv bash:
sudo apt-get install csvtool
(для систем на базе Debian)
Это вернет столбец с первой строкой с идентификатором.
csvtool namedcol ID csv_file.csv
Это вернет четвертую строку:
csvtool col 4 csv_file.csv
Если вы хотите удалить строку заголовка:
csvtool col 4 csv_file.csv | sed '1d'
Интересно, почему ни в одном из ответов до сих пор не упоминается csvkit.
csvkit - это набор инструментов командной строки для преобразования и работы с CSV.
Я использую его исключительно для управления данными csv, и пока я не нашел проблемы, которую я не мог бы решить с помощью cvskit.
Чтобы извлечь один или несколько столбцов из файла cvs, вы можете использовать csvcut
служебную программу, которая является частью набора инструментов. Чтобы извлечь второй столбец, используйте эту команду:
csvcut -c 2 filename_in.csv > filename_out.csv
Если строки в csv цитируются, добавьте символ кавычки с q
опцией:
csvcut -q '"' -c 2 filename_in.csv > filename_out.csv
Установите с помощью pip install csvkit
или sudo apt install csvkit
.
Вы не можете этого сделать без полноценного парсера CSV.
cut
?
Некоторое время я использовал этот код, он не будет «быстрым», если вы не посчитаете «вырезание и вставку из stackoverflow».
Вместо IFS в цикле используются операторы $ {##} и $ {%%}. Он вызывает 'err' и 'die' и поддерживает только запятую, тире и вертикальную черту в качестве символов SEP (это все, что мне нужно).
err() { echo "${0##*/}: Error:" "$@" >&2; }
die() { err "$@"; exit 1; }
# Return Nth field in a csv string, fields numbered starting with 1
csv_fldN() { fldN , "$1" "$2"; }
# Return Nth field in string of fields separated
# by SEP, fields numbered starting with 1
fldN() {
local me="fldN: "
local sep="$1"
local fldnum="$2"
local vals="$3"
case "$sep" in
-|,|\|) ;;
*) die "$me: arg1 sep: unsupported separator '$sep'" ;;
esac
case "$fldnum" in
[0-9]*) [ "$fldnum" -gt 0 ] || { err "$me: arg2 fldnum=$fldnum must be number greater or equal to 0."; return 1; } ;;
*) { err "$me: arg2 fldnum=$fldnum must be number"; return 1;} ;;
esac
[ -z "$vals" ] && err "$me: missing arg2 vals: list of '$sep' separated values" && return 1
fldnum=$(($fldnum - 1))
while [ $fldnum -gt 0 ] ; do
vals="${vals#*$sep}"
fldnum=$(($fldnum - 1))
done
echo ${vals%%$sep*}
}
Пример:
$ CSVLINE="example,fields with whitespace,field3"
$ $ for fno in $(seq 3); do echo field$fno: $(csv_fldN $fno "$CSVLINE"); done
field1: example
field2: fields with whitespace
field3: field3
Вы также можете использовать цикл while
IFS=,
while read name val; do
echo "............................"
echo Name: "$name"
done<itemlst.csv
echo '1,"2,3,4,5",6' | awk -F "\"*,\"*" '{print $2}'
будет печатать2
вместо2,3,4,5
.