解答

Sun Feb 1 15:02:32 JST 2015 (modified: Mon Aug 19 17:53:20 JST 2019)
views: 5464, keywords:コマンド,Linux,勉強会,シェル芸,シェル芸勉強会 この記事は最終更新日が4年以上前のものです。

【問題と解答例】第15回ドキッ!grepだらけのシェル芸勉強会

イントロのスライド

20150201 第15回シェル芸勉強会イントロ(ドキッ!grepだらけのシェル芸勉強会) from Ryuichi Ueda

諸注意

解答はUbuntu Linux 14.04で作成しました。コマンドがないときは適宜インストールのほど。

Macな人はbrewでGNU grep(ggrep)をインストールすると良かれ悪しかれ拡張オプションが使えます。インストール方法は例えばこちらが分かりやすいかと。3行で済みます。

Q1

次のようにファイルを作ります。

$ seq 2 5 > a
   $ seq 1 9 > b
   $ seq 5 11 > c
   $ seq 3 6 > d

1という文字を含まないファイルを列挙してください(aとdですね)。

解答

$ grep -L 1 {a..d}
   a
   d
   ###-Lを知らなければ###
   $ grep -c 1 {a..d} | awk -F: '$2==0'
   a:0
   d:0

Q2

作業ディレクトリを作り、その下に次のようにfile.1〜file.10000というファイルを作ります。

$ seq 1 10000 | xargs -I@ touch file.@

以下の数字を持つファイルだけ残して後のファイルを消去してください。

  • 1〜9
  • 10, 20, 30, ..., 90
  • 数字の下2桁が0のファイル

解答

$ ls -f | grep -v "file\\..$" | grep -v "file\\..0$" | grep -v "file\\..*00$" | xargs rm
   rm: cannot remove ‘.’: Is a directory
   rm: cannot remove ‘..’: Is a directory
   ###こんな書き方も###
   $ ls -f | 
   grep -v -e "file\\..$" -e "file\\..0$" -e "file\\..*00$" |
   xargs rm
   rm: cannot remove ‘.’: Is a directory
   rm: cannot remove ‘..’: Is a directory
   $ ls
   file.1 file.2300 file.4 file.5400 file.70 file.8500
   file.10 file.2400 file.40 file.5500 file.700 file.8600
   file.100 file.2500 file.400 file.5600 file.7000 file.8700
   file.1000 file.2600 file.4000 file.5700 file.7100 file.8800
   file.10000 file.2700 file.4100 file.5800 file.7200 file.8900
   file.1100 file.2800 file.4200 file.5900 file.7300 file.9
   file.1200 file.2900 file.4300 file.6 file.7400 file.90
   file.1300 file.3 file.4400 file.60 file.7500 file.900
   file.1400 file.30 file.4500 file.600 file.7600 file.9000
   file.1500 file.300 file.4600 file.6000 file.7700 file.9100
   file.1600 file.3000 file.4700 file.6100 file.7800 file.9200
   file.1700 file.3100 file.4800 file.6200 file.7900 file.9300
   file.1800 file.3200 file.4900 file.6300 file.8 file.9400
   file.1900 file.3300 file.5 file.6400 file.80 file.9500
   file.2 file.3400 file.50 file.6500 file.800 file.9600
   file.20 file.3500 file.500 file.6600 file.8000 file.9700
   file.200 file.3600 file.5000 file.6700 file.8100 file.9800
   file.2000 file.3700 file.5100 file.6800 file.8200 file.9900
   file.2100 file.3800 file.5200 file.6900 file.8300
   file.2200 file.3900 file.5300 file.7 file.8400

Q3

次のテキストから、「-v」、「-f」、「awk」の数をそれぞれカウントしてください。gawk、nawkは避けてください(awkの数としてカウントしない)。できる人はgrepは1個で。さらにできる人は拡張正規表現を使わないでやってみましょう。

$ cat text1 
   awk -v v="hoge" 'BEGIN{print v}'
   echo 'BEGIN{print 1}' | gawk -f -
   nawk 'BEGIN{print " BEGIN{print x}"}' | awk -v x=3 -f -

解答

###ベタな感じ(これでも全然問題ありません)###
   $ grep -oE '(-[a-z]|[a-z]?awk)' text1 | grep -v '[ng]awk' | sort | uniq 
   c- 2 -f
    2 -v
    2 awk
   ###最小手順(と思われる方法)###
   $ grep -wEo "(-[a-z]|awk)" text1 | sort | uniq 
   c- 2 -f
    2 -v
    2 awk
   ###拡張正規表現を使わない###
   $ grep -wo -e "-[a-z]" -e "awk" text1 | sort | uniq 
   c- 2 -f
    2 -v
    2 awk

Q4

/etc/の下(子、孫、・・・)のファイルのうち、シバンが「#!/bin/sh」のシェルスクリプトについて、中に「set -e」と記述のあるファイルとないファイルの数をそれぞれ数えてください。(コメント中のset -eも数えてOKです。)

解答

一例です。set -eと記述があるものが33、無いものが75となります。

$ sudo grep -l '#!/bin/sh' /etc/ -R | sudo xargs grep -c 'set -e' |
    sed 's/.*://' | awk '{if($1==0){print 0}else{print 1}}' | sort | uniq 
   c-grep: /etc/alternatives/ghostscript-current/Resource/CIDFSubst/DroidSansFallback.ttf: No such file or directory
   grep: /etc/blkid.tab: No such file or directory
    75 0
    33 1

Q5

日本語やギリシャ文字のある行を除去してください。

$ cat text2 
   A pen is a pen?
   日本語でおk
   ΩΩπ<Ω< na nandatte!!
   Randy W. Bass
   env x='() { :;}; echo vulnerable' bash -c "echo this is a test"
   #危険シェル芸

解答

別解求む。

$ LANG=C grep "^[[:print:]]*$" text2

Q6

次のようにファイルa, b, cを作ります。

$ echo 1 2 3 4 > a
   $ echo 2 3 4 5 > b
   $ echo 1 4 5 > c

ファイルの中の数字を足して10になるファイルを挙げてください。

解答

###grepを使わなくてもいけますが・・・###
   $ for i in a b c ; do [ 10 -eq $(numsum -r $i) ] && echo $i ; done
   ###grepでリストを作る###
   $ grep "" * | tr ':' ' ' | 
   awk '{for(i=2;i<=NF;i++){a+=$i};print $1,a;a=0}' | grep " 10$"
   ###Tukubaiを利用###
   $ grep "" * | tr ':' ' ' | ysum num=1 | grep " 10$"

Q7

psコマンドを打って(オプションは任意)、そのpsコマンドの行、親プロセスの行、親の親のプロセスの行を表示してみてください。

解答

すごくいい加減な気がしないでもありませんが・・・

$ ps -eo ppid,pid,command > f ; grep "ps -eo" f | grep -v grep |
    awk '{print " "$1" ";print " "$2" "}' | grep -f - f |
    awk '{print " "$1" ";print " "$2" "}' | grep -f - f
    5696 5767 sshd: ueda@pts/6 
    5767 5768 -bash
    5768 8806 ps -eo ppid,pid,command

Q8

seqとfactorの出力の後ろにgrepだけをいくつかつなげて、「素数の一つ前の数で、かつ10以上の数」を列挙してください。

$ seq 10 1000 | factor | ...(grepだけ)
$ seq 10 1000 | factor | grep -EB 1 '^[^ ]+ [^ ]+$' |
    grep -Eo '^[0-9]+[02468]:' | grep -Eo '^[0-9]+'
ノート   このエントリーをはてなブックマークに追加 
 

prev:【問題】第15回ドキッ!grepだらけのシェル芸勉強会 next:GlueLangのLTやった(シェル書いてますが何か?)

やり散らかし一覧

記事いろいろ