Возьмите n-й столбец в текстовом файле

86

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

1 Q0 1657 1 19.6117 Exp
1 Q0 1410 2 18.8302 Exp
2 Q0 3078 1 18.6695 Exp
2 Q0 2434 2 14.0508 Exp
2 Q0 3129 3 13.5495 Exp

Я хочу взять 2-е и 4-е слово в каждой строке следующим образом:

1657 19.6117
1410 18.8302
3078 18.6695
2434 14.0508
3129 13.5495

Я использую этот код:

 nol=$(cat "/path/of/my/text" | wc -l)
 x=1
 while  [ $x -le "$nol" ]
 do
     line=($(sed -n "$x"p /path/of/my/text)
     echo ""${line[1]}" "${line[3]}""  >> out.txt
     x=$(( $x + 1 ))
 done

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

Есть способ сделать это попроще?

mnrl
источник
1
2-е слово каждой строки называется просто 2-м столбцом!
Бернард

Ответы:

127

iirc:

cat filename.txt | awk '{ print $2 $4 }'

или, как указано в комментариях:

awk '{ print $2 $4 }' filename.txt
Том ван дер Вердт
источник
16
УУОК !!! awk '{print $2,$4}' filename.txtлучше (нет трубы, вызывается только одна программа)
синий
5
@blue Я часто использую catв своих сценариях bash вместо указания имени файла, потому что накладные расходы минимальны и потому что синтаксис cat ... | ... > ...действительно хорошо показывает, что такое ввод и куда идет вывод. Но вы правы, здесь это на самом деле не нужно.
Tom van der Woerdt
8
@TomvanderWoerdt: Иногда я пишу < input awk '{ print $2 $4 }' > outputдля этого.
ruakh
69

Вы можете использовать cutкоманду:

cut -d' ' -f3,5 < datafile.txt

печатает

1657 19.6117
1410 18.8302
3078 18.6695
2434 14.0508
3129 13.5495

то

  • -d' '- значит, использовать spaceкак разделитель
  • -f3,5 - взять и распечатать 3-й и 5-й столбцы

cutЭто намного быстрее для больших файлов в качестве чистого раствора оболочки. Если ваш файл разделен несколькими пробелами, вы можете сначала удалить их, например:

sed 's/[\t ][\t ]*/ /g' < datafile.txt | cut -d' ' -f3,5

где (gnu) sed заменяет любые символы tabили spaceна один space.

Для варианта - вот и perl-решение:

perl -lanE 'say "$F[2] $F[4]"' < datafile.txt
jm666
источник
1
Работает хорошо ... если вам гарантировано такое количество пробелов в каждой строке, точно ... :)
rogerdpack
24

Для полноты:

while read _ _ one _ two _; do
    echo "$one $two"
done < file.txt

Вместо _произвольной переменной (например, junk) также можно использовать. Дело в том, чтобы просто извлечь столбцы.

Демо:

$ while read _ _ one _ two _; do echo "$one $two"; done < /tmp/file.txt
1657 19.6117
1410 18.8302
3078 18.6695
2434 14.0508
3129 13.5495
Йоханнес Вайс
источник
Приятный, читаемый и не требующий perls / awks / других, все в одной оболочке с помощью встроенных команд.
Петр Матусу
6

Еще один простой вариант -

$ while read line
  do
      set $line          # assigns words in line to positional parameters
      echo "$3 $5"
  done < file
AKA11
источник
4

Если ваш файл содержит n строк, ваш скрипт должен прочитать файл n раз; поэтому, если вы удвоите длину файла, вы в четыре раза увеличите объем работы, выполняемой вашим скриптом - и почти вся эта работа будет просто выброшена, поскольку все, что вам нужно сделать, это перебрать строки по порядку.

Вместо этого лучший способ перебрать строки файла - использовать whileцикл, в котором команда-условие является readвстроенной:

while IFS= read -r line ; do
    # $line is a single line of the file, as a single string
    : ... commands that use $line ...
done < input_file.txt

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

while read -r -a line ; do
    echo ""${line[1]}" "${line[3]}"" >> out.txt
done < /path/of/my/text

или еще лучше:

while read -r -a line ; do
    echo "${line[1]} ${line[3]}"
done < /path/of/my/text > out.txt

Однако для того, что вы делаете, вы можете просто использовать cutутилиту:

cut -d' ' -f2,4 < /path/of/my/text > out.txt

(или awk, как предполагает Том ван дер Вердт, perlили даже sed).

руах
источник
предпочли бы readболее , cutпотому что это прочный против нескольких пространств между полями и вам не нужно волшебство массива:while read word1 word2 word3 word4 rest; do doSomethingWith $word2 $word4; done
user829755
3

Если вы используете структурированные данные, это дает дополнительное преимущество, заключающееся в том, что вы не запускаете дополнительный процесс оболочки trи / cutили что-то еще. ...

(Конечно, вы захотите защититься от неправильных входных данных с помощью условных выражений и разумных альтернатив.)

...
while read line ; 
do 
    lineCols=( $line ) ;
    echo "${lineCols[0]}"
    echo "${lineCols[1]}"
done < $myFQFileToRead ; 
...
Инги
источник