
cities
次のファイルがあります。
[1598] San Diego, US (inactive)
[4517] St Louis, US (inactive)
[6346] Orlando, US (inactive)
私は次のように都市名を切り取りたいと思います。
San Diego
St Louis
Orlando
これが私が思いつく最善の方法です:
cut -d ',' -f1 cities | cut -d ']' -f2
しかし、名前の前にはまだ空白が残っています。cut
続行できるように複数の文字を区切り文字として受け入れる同様のコマンドはありますか]
?
答え1
アッ(また確認奇妙な情報)この種の問題には美しいです。努力する:
awk -F'[],] *' '{print $2}' cities
これはフィールド区切り文字を-F
次のように定義します[],] *
。これは、閉じ括弧またはカンマが一度発生し、その後に空白がゼロまたは複数が続くことを意味します。もちろん、どんな要件にも合わせて変更できます。正規表現を読んでください。
行を分割したら、分割結果に対して必要な操作を実行できます。ここでは、print 2番目のフィールドを使用することにしましたprint $2
。 awkディレクティブの周りに一重引用符を使用することが重要です。それ以外の場合、$ 2はシェルに置き換えられます。
答え2
cut
パイプラインの最後のエントリを変更することで、次のことができます。
cut -d ' ' -f2-
上記の意味は、フィールド区切り文字が空白で、2番目のフィールドから始まり、すべてのフィールドを選択することです。全体の順序は次のとおりです。
cut -d ',' -f1 cities | cut -d ' ' -f2-
答え3
答え4
sed と grep が難しすぎる場合は、主に Perl を使用します。
Perlでこれを書く方法はいくつかあります。たとえば、高速化したい場合や、入力で予期しないマイナーな問題(たとえば、2つのスペースが予想される場合)を処理したい場合があります。
明確なアプローチ(idは数値、cityは文字、stateは文字であると仮定):
while (<>) {
if (/^\[\d+\] (\w+(?: \w+)*), \w+ \(\w*\)$/) {
my $city = $1;
print "$city\n";
}
}
または遅いですが、より寛大です(より多くの逆追跡を実行します)。
while (<>) {
if (/^.*\]\s+(.*),.*$/) {
my $city = $1;
print "$city\n";
}
}
または、はるかに高速です(フィールドは閉じ括弧が最初に表示されたときに停止します)。
while (<>) {
if (/^\[[^]]*\] ([^,]*), \S+ \([^)]*\)$/) {
my $city = $1;
print "$city\n";
}
}
スクリプトの代わりに、コマンドラインでデフォルトでループを-n
追加するこのオプションを使用できます。while (<>) { BLOCK }
perl -ne '/^\[[^]]*\] ([^,]*), \S+ \([^)]*\)$/ and print $1, "\n";' cities
または、cutと同様の使い方が必要な場合は、-F
awkのオプションと同様のこのオプションを使用できます-F
。たとえば、次のようになります。
perl -a -n -F'/[],]\s+/' -e 'print $F[1], "\n"' cities
この方法は、どのフィールドにも区切り文字が含まれていないことを明確に想定しています。