GNU/Linux >> Linux の 問題 >  >> Linux

ファイルまたはスクリプト内のパイプから複数の行を選択する方法は?

この awk を使用できます:

awk -v s='2,4' 'BEGIN{split(s, a, ","); for (i in a) b[a[i]]} NR in b' file
two
four

別のスクリプト lines.sh 経由 :

#!/bin/bash
awk -v s="$1" 'BEGIN{split(s, a, ","); for (i in a) b[a[i]]} NR in b' "$2"

次に、実行権限を付与します:

chmod +x lines.sh

そしてそれを次のように呼び出します:

./lines.sh '2,4' 'test.txt'

sed を試す :

sed -n '2p; 4p' inputFile

-n sed に伝えます 出力を抑制しますが、行 2 に対して および 4p (print) コマンドを使用して、これらの行を印刷します。

範囲を使用することもできます。例:

sed -n '2,4p' inputFile

2 つの純粋な Bash バージョン。一般的で再利用可能なソリューションを探しているので、それに少し力を入れたほうがよいでしょう。 (前のセクションも参照してください)。

バージョン 1

このスクリプトは、標準入力全体を配列に丸呑みします (mapfile を使用) であるため、かなり効率的です)、引数で指定された行を出力します。範囲は有効です。例:

1-4 # for lines 1, 2, 3 and 4
3-  # for everything from line 3 till the end of the file

これらはスペースまたはコンマで区切ることができます。行は、引数が指定された順序で正確に出力されます:

lines 1 1,2,4,1-3,4- 1

行 1 を 2 回印刷し、次に行 2、次に行 4、次に行 1、2、および 3、次に行 4 から最後まですべてを印刷し、最後に行 1 をもう一度印刷します。

どうぞ:

#!/bin/bash

lines=()

# Slurp stdin in array
mapfile -O1 -t lines

# Arguments:
IFS=', ' read -ra args <<< "$*"

for arg in "${args[@]}"; do
   if [[ $arg = +([[:digit:]]) ]]; then
      arg=$arg-$arg
   fi
   if [[ $arg =~ ([[:digit:]]+)-([[:digit:]]*) ]]; then
      ((from=10#${BASH_REMATCH[1]}))
      ((to=10#${BASH_REMATCH[2]:-$((${#lines[@]}))}))
      ((from==0)) && from=1
      ((to>=${#lines[@]})) && to=${#lines[@]}
      ((from<=to)) || printf >&2 'Argument %d-%d: lines not in increasing order' "$from" "$to"
      for((i=from;i<=to;++i)); do
         printf '%s\n' "${lines[i]}"
      done
   else
      printf >&2 "Error in argument \`%s'.\n" "$arg"
   fi
done
  • プロ:本当にクールです。
  • 短所:ストリーム全体をメモリに読み込む必要があります。無限ストリームには適していません。

バージョン 2

このバージョンは、以前の無限ストリームの問題に対処しています。ただし、行を繰り返したり並べ替えたりすることはできなくなります。

同じこと、範囲が許可されています:

lines 1 1,4-6 9-

行 1、4、5、6、9 と最後まですべてを出力します。行のセットが制限されている場合、最後の行が読み取られるとすぐに終了します。

#!/bin/bash

lines=()
tillend=0
maxline=0

# Process arguments
IFS=', ' read -ra args <<< "[email protected]"

for arg in "${args[@]}"; do
   if [[ $arg = +([[:digit:]]) ]]; then
       arg=$arg-$arg
   fi
   if [[ $arg =~ ([[:digit:]]+)-([[:digit:]]*) ]]; then
      ((from=10#${BASH_REMATCH[1]}))
      ((from==0)) && from=1
      ((tillend && from>=tillend)) && continue
      if [[ -z ${BASH_REMATCH[2]} ]]; then
         tillend=$from
         continue
      fi
      ((to=10#${BASH_REMATCH[2]}))
      if ((from>to)); then
         printf >&2 "Invalid lines order: %s\n" "$arg"
         exit 1
      fi
      ((maxline<to)) && maxline=$to
      for ((i=from;i<=to;++i)); do
         lines[i]=1
      done
   else
      printf >&2 "Invalid argument \`%s'\n" "$arg"
      exit 1
   fi
done

# If nothing to read, exit
((tillend==0 && ${#lines[@]}==0)) && exit

# Now read stdin
linenb=0
while IFS= read -r line; do
   ((++linenb))
   ((tillend==0 && maxline && linenb>maxline)) && exit
   if [[ ${lines[linenb]} ]] || ((tillend && linenb>=tillend)); then
      printf '%s\n' "$line"
   fi
done
  • 長所:これは非常に優れており、メモリ内のストリーム全体を読み取ることはありません。
  • 短所:バージョン 1 のように行を繰り返したり、並べ替えたりすることはできません。スピードではなく、その長所です。

さらなる考察

バージョン 1 やバージョン 2 と同じ機能を備えた素晴らしい汎用スクリプトが本当に必要な場合は、Perl などの別の言語を使用することを検討する必要があります。多くの (特に速度) が得られます。よりクールなことをたくさん行う素敵なオプションを持つことができます。一般的で再利用可能なスクリプトが必要なため、長期的には価値があるかもしれません。メールを読み取るスクリプトができあがるかもしれません!

免責事項 これらのスクリプトを完全にチェックしていないので、バグに気をつけてください!


Linux
  1. 別のファイルにIDがリストされているテキストファイルから行を選択しますか?

  2. Sedを使用してテキストファイルから複数のランダム行を削除する方法は?

  3. Linux 設定ファイルの複数行をコメントアウトするには?

  1. nano を使用してファイルからすべてのテキストを選択するにはどうすればよいですか?

  2. ファイルを行単位で切り詰めるには?

  3. Linuxでテキストファイルから特定の行を表示するには?

  1. ファイル(タブとスペースを含む)から空白行を削除する方法は?

  2. シェルスクリプトで一時ファイルを作成するにはどうすればよいですか?

  3. Awkからファイルをキャットする方法は?