Как извлечь поля для определенных номеров строк, которые передаются в качестве параметров сценарию bash?

Можно обернуть fold, затем headего:

onepage () {
  fold -w "$(tput cols)" -s "$@" |
    head -n "$(($(tput lines)-1))"
}

Или, может быть, используйте pr. Предполагая, что GNUpr:

pr -l "$((LINES - 1))" +1:1 -t
  • -l...-установить высоту страницы на$LINES-1.
  • +1:1начать печать с первой страницы... до первой страницы.
  • -t-не печатать заголовок.

0
04.05.2021, 20:12
5 ответов
#! /bin/bash
# get.sh
IFS=$'\n'
args=(`sort -nu <<<$*`)
unset IFS

awk -v lines="${args[*]}" 'BEGIN{split(lines, ar, " ");}{ for (i in ar) { if (NR == ar[i]) print $1,$2} }' data.txt

Сначала создается массив args, содержащий отсортированные и уникальные значения. Для этого мы использовали опции -nи -u. Посмотреть подробнее деталь .

Затем splitсоздает массив arиз переменной lines. Теперь для цикла вывести требуемый вывод, если элемент в arравен записи номер (NR ).

0
28.07.2021, 11:35

В awk вы могли собирать номера строк в массив и читать файл один раз, печатая строки, упомянутые в массиве:

#!/bin/sh
awk -v lines="$*" 'BEGIN { split(lines, a, "[, ]"); 
                           for (i in a) b[a[i]] = 1;}
                   NR in b {print $1, $2}' < data.txt

Цикл split()разбивает переменную linesпо пробелам и запятым в массив a, а цикл forстроит массив bтаким образом, что ключи этого массива содержат интересующие нас строки. Затем NR in bпросто проверяет, существует ли ключ, соответствующий текущему номеру строки.

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

$ bash get.sh 7 3 3
3 cFDLKSFD_FDSJFskadfsff_fsadklfj_fdsaf
7 gFDLKSFD_FDSJFskadfsff_fsadklfj_fdsaf

(get.sh 7,3,3тоже работает)

5
28.07.2021, 11:35

Поместите приведенные ниже строки в текстовый файл и назовите его get.sh. затем сделайте его исполняемым.

#!/bin/sh
## this is GNU sed
sed -En "
  $(printf '%sbp\n' "$@" "d;")
  :p;s/\S+/&\n/2;P
" data.txt

Теперь вызовите скрипт, как показано:

chmod +x./get.sh
./get.sh 1 3 5
1
28.07.2021, 11:35

Следующий bashскрипт создает скрипт sed, который изменяет и отображает строки по номерам, указанным в командной строке:

#!/bin/bash

sed_script=()
for lineno do
        sed_script+=( -e "$lineno ba" )
done

sed "${sed_script[@]}" \
        -e 'd' -e ':a' -e 's/[[:blank:]]*[^[:blank:]]*$//' <data.txt

или, для/bin/sh:

#!/bin/sh

for lineno do
    set -- "$@" -e "$lineno ba"
    shift
done

sed "$@" -e 'd' -e ':a' -e 's/[[:blank:]]*[^[:blank:]]*$//' <data.txt

При выполнении как ./get.sh 1 3 5оба этих скрипта завершатся выполнением команды

sed -e '1 ba' -e '3 ba' -e '5 ba' -e d -e :a -e 's/[[:blank:]]*[^[:blank:]]*$//' <data.txt

Это соответствует скрипту sed

1 ba
3 ba
5 ba
d
:a
s/[[:blank:]]*[^[:blank:]]*$//

Подстановка s/[[:blank:]]*[^[:blank:]]*$//удалит последний столбец в данных (последняя серия пробелов или табуляции следует за серией не -пробелов и не -табуляций в конце строки ). Остальная часть кода sedгарантирует, что выполнение переходит к метке :a, если мы находимся на одной из строк, которые мы хотим вывести (это то, что делает ba), и что в противном случае текущая строка удаляется с помощью d.

Вы можете изменить это и удалить жестко закодированное -перенаправление из data.txtв скрипте. Вместо этого вы можете перенаправить любой поток данных, который хотите обработать :

.
$./get.sh 1 2 9 <data.txt
 1           aFDLKSFD_FDSJFskadfsff_fsadklfj_fdsaf
 2           bFDLKSFD_FDSJFskadfsff_fsadklfj_fdsaf
 9           iFDLKSFD_FDSJFskadfsff_fsadklfj_fdsaf

Аналогичный подход вawk:Дайте коду awkкучу пробелов -номера строк с разделителями из командной строки скрипта (приведенный ниже код получает их из linenoпеременной окружения, установленной вызов оболочки ). Распакуйте их в ключи в таблице поиска(lines)в блоке BEGIN, а затем просто проверьте, является лиFNR(текущий номер строки в текущем файле )ключом в этой таблице поиска.

#!/bin/sh

lineno="$*" awk '
        BEGIN {
                n = split(ENVIRON["lineno"], a, " ")
                for (i = 1; i <= n; ++i)
                        lines[a[i]] = 1
        }

        FNR in lines { print $1, $2 }'

(FNR in linesможно заменить на lines[FNR], чтобы единицы, которые мы сохраняем как значения, действительно стали полезными.)

Тестирование:

$./get.sh 3 4 8 <data.txt
 3           cFDLKSFD_FDSJFskadfsff_fsadklfj_fdsaf
 4           dFDLKSFD_FDSJFskadfsff_fsadklfj_fdsaf
 8           hFDLKSFD_FDSJFskadfsff_fsadklfj_fdsaf
1
28.07.2021, 11:35
#!/bin/bash

perl -le '
for (@ARGV) {
  # separate command line args into filename(s) and line-number(s)
  # line-numbers can be space and/or comma separated.
  if (-e $_) { push @files, $_ } else { push @lines, split /,/};
};

@ARGV = @files;
$re = join("|",@lines);

while(<>) {
  print join("\t",(split)[0..1]) if ($. =~ m/^($re)$/);
  close(ARGV) if eof;
}' "$@"

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

close(ARGV)нужен только потому, что нас интересует номер строки текущего файла, а не номер строки -всех входных данных, просмотренных до сих пор. perl сбрасывает переменную $.(, также известную как $NRили $INPUT_LINE_NUMBER), только когда дескриптор файла закрывается, но дескрипторы файлов обычно не закрываются в цикле while(<>).Это просто явно закрывает дескриптор файла, так что $.сбрасывается. См. perldoc -f eof.

$./get.sh 1 3,5 data.txt 
1       aFDLKSFD_FDSJFskadfsff_fsadklfj_fdsaf
3       cFDLKSFD_FDSJFskadfsff_fsadklfj_fdsaf
5       eFDLKSFD_FDSJFskadfsff_fsadklfj_fdsaf

Этот скрипт, конечно же, должен быть Perl-скриптом, а не бессмысленной bash-оболочкой Perl «одного -лайнера». Но люди, кажется, думают, что один лайнер -является "правильным", в то время как скрипты, использующие что-либо, кроме #!/bin/bash или #!/bin/sh в качестве интерпретатора, в чем-то неверны.

#!/usr/bin/perl -l

for (@ARGV) {
  # separate command line args into filename(s) and line-number(s)
  # line-numbers can be space and/or comma separated.
  if (-e $_) { push @files, $_ } else { push @lines, split /,/ };
};

@ARGV = @files;
$re = join('|',@lines);

while(<>) {
  print join("\t",(split)[0..1]) if ($. =~ m/^($re)$/);
  close(ARGV) if eof;
};
$./get.pl 1 3,5 data.txt  
1       aFDLKSFD_FDSJFskadfsff_fsadklfj_fdsaf
3       cFDLKSFD_FDSJFskadfsff_fsadklfj_fdsaf
5       eFDLKSFD_FDSJFskadfsff_fsadklfj_fdsaf

Это делает в точности то же самое, не тратя впустую ничтожное количество времени и памяти на разветвление интерпретатора оболочки, который ничего не делает, кроме разветвления интерпретатора perl.

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

1
28.07.2021, 11:35

Теги

Похожие вопросы