103.2 レッスン 1
Certificate: |
LPIC-1 |
---|---|
Version: |
5.0 |
Topic: |
103 GNUおよびUnixコマンド |
Objective: |
103.2 テキストストリームをフィルターで処理する |
Lesson: |
1 of 1 |
はじめに
すべてのシステム管理者にとって、テキストの処理は重要な仕事の一つです。Unix開発チームのオリジナルメンバーであるDoug McIlroyは、Unix哲学を要約し、(とりわけ重要なこととして)“テキストストリームを扱うプログラムを書きなさい、なぜならそれが普遍的なインターフェイスだからです。” と述べています。LinuxはUnixオペレーティングシステムに触発され、その哲学をしっかりと引き継いでいるため、Linuxディストリビューションには多くのテキスト操作ツールが含まれています。
リダイレクトとパイプの簡単な紹介
(※訳注: 詳細は「103.4 ストリームとパイプ、リダイレクトを使う」で説明します。)
Unix哲学より抜粋;
-
1つのことをうまく実行するプログラムを作成しよう。
-
連携できるようにプログラムを作成しよう。
プログラムを連携させる主な方法の1つは、パイプ と リダイレクト を使用することです。ほとんどすべてのテキスト操作プログラムは、標準入力(stdin)からテキストを取得し、処理結果を標準出力(stdout)に出力し、さらにエラーメッセージを標準エラー(stderr)に出力します。特に指定しない限り、標準入力はキーボードからの入力です(Enterキーを押すたびにプログラムが読み取ります)。標準出力とエラー出力が、端末画面に表示されます。これらの働きを見ていきましょう。
ターミナルで cat
と入力し、Enterキーを押します。次に、ランダムなテキストを入力します。
$ cat This is a test This is a test Hey! Hey! It is repeating everything I type! It is repeating everything I type! (I will hit ctrl+c so I will stop this nonsense) (I will hit ctrl+c so I will stop this nonsense) ^C
cat
コマンド(“concatenate” に由来)の詳細は、manページを参照してください。
Note
|
最小構成でインストールしたLinuxサーバーでは、 |
例に示したように、cat
で読み取り先を指定しない場合、入力した内容(標準入力)は、そのままターミナルウィンドウ(標準出力)に出力されます。
では、次を試してみましょう。
$ cat > mytextfile This is a test I hope cat is storing this to mytextfile as I redirected the output I will hit ctrl+c now and check this ^C $ cat mytextfile This is a test I hope cat is storing this to mytextfile as I redirected the output I will hit ctrl+c now and check this
>
(大なり記号)は、cat
の標準出力を mytextfile
ファイルに送るように(シェルに)指示します。続いてこれを試してみましょう。
$ cat mytextfile > mynewtextfile $ cat mynewtextfile This is a test I hope cat is storing this to mytextfile as I redirected the output I will hit ctrl+c now and check this
これは、mytextfile
を mynewtextfile
にコピーします。diff
を実行することで、これら2つのファイルの内容が同じであることを確認できます。
$ diff mynewtextfile mytextfile
実行結果に何も出力されないので、ファイルは同一です。次に、追記リダイレクト(>>
)を試してみましょう。
$ echo 'This is my new line' >> mynewtextfile $ diff mynewtextfile mytextfile 4d3 < This is my new line
ここまでは、リダイレクトを使用してファイルを作成および操作してきました。次に、パイプ(記号 |
)を使用して、あるプログラムの出力を別のプログラムにリダイレクトしてみましょう。“this” という単語が含まれている行を見つけてみます。
$ cat mytextfile | grep this I hope cat is storing this to mytextfile as I redirected the output I will hit ctrl+c now and check this $ cat mytextfile | grep -i this This is a test I hope cat is storing this to mytextfile as I redirected the output I will hit ctrl+c now and check this
この例では、cat
の標準出力を、別のコマンド grep
にパイプしています。-i
オプションで大文字、小文字を区別しないように指示すると、出力結果が変化します。
テキストストリームの処理
圧縮ファイルの読み取り
以下のコマンドリストを含む ftu.txt
というファイルを作成します。(訳注: ファイル名 ftu
は出題範囲にあるタイトル files, terms and utilities に由来しています。すなわち、このレッスンで出題される可能性が高いコマンド名のリストです。)
bzcat cat cut head less md5sum nl od paste sed sha256sum sha512sum sort split tail tr uniq wc xzcat zcat
次に、grep
コマンドを使用して、文字列 cat
を含むすべての行を出力します。
$ cat ftu.txt | grep cat bzcat cat xzcat zcat
grep
コマンドでテキストを直接フィルタリングしても、同じ結果が得られます。cat
コマンドでテキストストリームを stdout
に送る必要はありません。
$ grep cat ftu.txt bzcat cat xzcat zcat
Note
|
Linuxでは、同じタスクを処理する方法がたくさんあることを覚えておいてください。 |
ファイルを圧縮するコマンドには gzip
、bzip2
、xz
などがありますが、それぞれに圧縮ファイルの内容を表示するコマンドが用意されています。gzip
には zcat
、bzip2
には bzcat
、xz
には xzcat
がそれぞれ対応します。
新しく作成したファイル ftu.txt
がディレクトリにあることを確認してから、gzip
圧縮ファイルを作成します。
$ ls ftu* ftu.txt $ gzip ftu.txt $ ls ftu* ftu.txt.gz
次に、zcat
コマンドを使用して、gzip圧縮された圧縮ファイルの内容を表示してみましょう。
$ zcat ftu.txt.gz bzcat cat cut head less md5sum nl od paste sed sha256sum sha512sum sort split tail tr uniq wc xzcat zcat
gzip
は ftu.txt
を ftu.txt.gz
に圧縮した後、元のファイルを削除することに注意してください。デフォルトでは、gzip
コマンドの処理内容は表示されません。gzip
の処理内容を確認したい場合は、-v
オプション(“verbose”)で冗長な出力を指定します。
ページャーでファイルを表示する
cat
コマンドの後にファイルを指定すると、その内容を標準出力に送ることはご存じでしょう。ファイル /var/log/syslog
には、Linuxシステムで起きている重要な事柄が保存されます。(sudo
コマンドを使用して特権を得ると)/var/log/syslog
ファイルを読み取れます。(訳注: /var/log/syslogのパーミッションはディストリビューションによって異なり、多くの場合は一般ユーザーでも読み出せますから、その場合は sudo
は必要ありません。)
$ sudo cat /var/log/syslog
…ターミナルウィンドウ内でメッセージが非常に高速にスクロールすることでしょう。出力プログラムの1つである less
にパイプすると、結果をページ送りすることができるようになります。less
を使用すると、メッセージをスクロールしたり、vi
エディタを使った時のようにテキスト全体を見渡したり検索することができます。(訳注: less
の操作方法は、vi
エディタとほぼ同じです。103.8 を参照してください。)
もっとも、cat
コマンドをページャーにパイプするよりも、ページャーを直接使用する方が実用的です。
$ sudo less /var/log/syslog ... (出力省略)
テキストファイルの一部を取り出す
ファイルの先頭ないし末尾のみを取り出したい時には、別のコマンドを使います。head
コマンドは、デフォルトでファイルの先頭10行を読み出し、tail
コマンドは、デフォルトでファイルの末尾10行を読み出します。試してみましょう。
$ sudo head /var/log/syslog Nov 12 08:04:30 hypatia rsyslogd: [origin software="rsyslogd" swVersion="8.1910.0" x-pid="811" x-info="https://www.rsyslog.com"] rsyslogd was HUPed Nov 12 08:04:30 hypatia systemd[1]: logrotate.service: Succeeded. Nov 12 08:04:30 hypatia systemd[1]: Started Rotate log files. Nov 12 08:04:30 hypatia vdr: [928] video directory scanner thread started (pid=882, tid=928, prio=low) Nov 12 08:04:30 hypatia vdr: [882] registered source parameters for 'A - ATSC' Nov 12 08:04:30 hypatia vdr: [882] registered source parameters for 'C - DVB-C' Nov 12 08:04:30 hypatia vdr: [882] registered source parameters for 'S - DVB-S' Nov 12 08:04:30 hypatia vdr: [882] registered source parameters for 'T - DVB-T' Nov 12 08:04:30 hypatia vdr[882]: vdr: no primary device found - using first device! Nov 12 08:04:30 hypatia vdr: [929] epg data reader thread started (pid=882, tid=929, prio=high) $ sudo tail /var/log/syslog Nov 13 10:24:45 hypatia kernel: [ 8001.679238] mce: CPU7: Core temperature/speed normal Nov 13 10:24:46 hypatia dbus-daemon[2023]: [session uid=1000 pid=2023] Activating via systemd: service name='org.freedesktop.Tracker1.Miner.Extract' unit='tracker-extract.service' requested by ':1.73' (uid=1000 pid=2425 comm="/usr/lib/tracker/tracker-miner-fs ") Nov 13 10:24:46 hypatia systemd[2004]: Starting Tracker metadata extractor... Nov 13 10:24:47 hypatia dbus-daemon[2023]: [session uid=1000 pid=2023] Successfully activated service 'org.freedesktop.Tracker1.Miner.Extract' Nov 13 10:24:47 hypatia systemd[2004]: Started Tracker metadata extractor. Nov 13 10:24:54 hypatia kernel: [ 8010.462227] mce: CPU0: Core temperature above threshold, cpu clock throttled (total events = 502907) Nov 13 10:24:54 hypatia kernel: [ 8010.462228] mce: CPU4: Core temperature above threshold, cpu clock throttled (total events = 502911) Nov 13 10:24:54 hypatia kernel: [ 8010.469221] mce: CPU0: Core temperature/speed normal Nov 13 10:24:54 hypatia kernel: [ 8010.469222] mce: CPU4: Core temperature/speed normal Nov 13 10:25:03 hypatia systemd[2004]: tracker-extract.service: Succeeded.
表示される行数をわかりやすくするために、head
コマンドの出力を nl
コマンドにパイプすると、コマンドに送られたテキストに行番号が表示されます。
$ sudo head /var/log/syslog | nl 1 Nov 12 08:04:30 hypatia rsyslogd: [origin software="rsyslogd" swVersion="8.1910.0" x-pid="811" x-info="https://www.rsyslog.com"] rsyslogd was HUPed 2 Nov 12 08:04:30 hypatia systemd[1]: logrotate.service: Succeeded. 3 Nov 12 08:04:30 hypatia systemd[1]: Started Rotate log files. 4 Nov 12 08:04:30 hypatia vdr: [928] video directory scanner thread started (pid=882, tid=928, prio=low) 5 Nov 12 08:04:30 hypatia vdr: [882] registered source parameters for 'A - ATSC' 6 Nov 12 08:04:30 hypatia vdr: [882] registered source parameters for 'C - DVB-C' 7 Nov 12 08:04:30 hypatia vdr: [882] registered source parameters for 'S - DVB-S' 8 Nov 12 08:04:30 hypatia vdr: [882] registered source parameters for 'T - DVB-T' 9 Nov 12 08:04:30 hypatia vdr[882]: vdr: no primary device found - using first device! 10 Nov 12 08:04:30 hypatia vdr: [929] epg data reader thread started (pid=882, tid=929, prio=high)
また、tail
コマンドの出力を wc
コマンドにパイプすることで、行数やワード数などを確認することができます。このコマンドに -l
オプションを指定すると、行数のみをカウントします。
$ sudo tail /var/log/syslog | wc -l 10
ファイルの先頭または末尾から、より多く(あるいは少なく)の行を取り出したいときは、-n
オプションに取り出す行数を指定します。
$ sudo tail -n 5 /var/log/syslog Nov 13 10:37:24 hypatia systemd[2004]: tracker-extract.service: Succeeded. Nov 13 10:37:42 hypatia dbus-daemon[2023]: [session uid=1000 pid=2023] Activating via systemd: service name='org.freedesktop.Tracker1.Miner.Extract' unit='tracker-extract.service' requested by ':1.73' (uid=1000 pid=2425 comm="/usr/lib/tracker/tracker-miner-fs ") Nov 13 10:37:42 hypatia systemd[2004]: Starting Tracker metadata extractor... Nov 13 10:37:43 hypatia dbus-daemon[2023]: [session uid=1000 pid=2023] Successfully activated service 'org.freedesktop.Tracker1.Miner.Extract' Nov 13 10:37:43 hypatia systemd[2004]: Started Tracker metadata extractor. $ sudo head -n 12 /var/log/syslog Nov 12 08:04:30 hypatia rsyslogd: [origin software="rsyslogd" swVersion="8.1910.0" x-pid="811" x-info="https://www.rsyslog.com"] rsyslogd was HUPed Nov 12 08:04:30 hypatia systemd[1]: logrotate.service: Succeeded. Nov 12 08:04:30 hypatia systemd[1]: Started Rotate log files. Nov 12 08:04:30 hypatia vdr: [928] video directory scanner thread started (pid=882, tid=928, prio=low) Nov 12 08:04:30 hypatia vdr: [882] registered source parameters for 'A - ATSC' Nov 12 08:04:30 hypatia vdr: [882] registered source parameters for 'C - DVB-C' Nov 12 08:04:30 hypatia vdr: [882] registered source parameters for 'S - DVB-S' Nov 12 08:04:30 hypatia vdr: [882] registered source parameters for 'T - DVB-T' Nov 12 08:04:30 hypatia vdr[882]: vdr: no primary device found - using first device! Nov 12 08:04:30 hypatia vdr: [929] epg data reader thread started (pid=882, tid=929, prio=high) Nov 12 08:04:30 hypatia vdr: [882] no DVB device found Nov 12 08:04:30 hypatia vdr: [882] initializing plugin: vnsiserver (1.8.0): VDR-Network-Streaming-Interface (VNSI) Server
ストリームエディタ sedの基本
今度は名前に cat
が含まれていない単語を見てみましょう。grep
に -v
オプションを指定して、cat
を 含まない 行のみを出力します。
$ zcat ftu.txt.gz | grep -v cat cut head less md5sum nl od paste sed sha256sum sha512sum sort split tail tr uniq wc
grep
でできることのほとんどは、sed
でも行えます。このコマンドは、テキストをフィルタしたり変換するためのストリームエディタです(sed
のマニュアルページにそう書かれています)。まず、gzip
圧縮ファイルを伸張して、元の ftu.txt
ファイルを復元します。
$ gunzip ftu.txt.gz $ ls ftu* ftu.txt
次のように、sed
で文字列 cat
を含む行のみを表示できます。
$ sed -n /cat/p < ftu.txt bzcat cat xzcat zcat
小なり記号 <
を使用して、ファイル ftu.txt
の内容を sed
コマンドに送っています。スラッシュで囲まれた単語(つまり、cat
)が、検索対象となる単語です。-n
オプションは sed
に、(p
コマンドで指示された場合を除いて)何も出力しないように指示するものです。同じコマンドを -n
オプションなしで実行するとどうなるか見てみましょう。
$ sed /cat/d < ftu.txt cut head less md5sum nl od paste sed sha256sum sha512sum sort split tail tr uniq wc
-n
オプションを指定しない場合、sed
は d
コマンドによって削除を指示された行(すなわち cat
を含む行)を除き、すべての行を表示します。
sed
の最も一般的な使用法は、ファイルから指定したテキストを見つけて置き換えることです。例えば、すべての cat
を dog
に変更するとしましょう。sed
でこれを行うには、s
コマンド(substituteに由来)を指定して、置換対象の cat
を、2番目のワード dog
に置換します。
$ sed s/cat/dog/ < ftu.txt bzdog dog cut head less md5sum nl od paste sed sha256sum sha512sum sort split tail tr uniq wc xzdog zdog
リダイレクト(<
)で ftu.txt
ファイルを sed
コマンドに渡すのではなく、sed
コマンドでファイルを直接操作することもできます。元のファイルのバックアップを作成してから、直接操作を試してみましょう。
$ sed -i.backup s/cat/dog/ ftu.txt $ ls ftu* ftu.txt ftu.txt.backup
sed
に -i
オプション(in-placeに由来)を指定すると、指定したファイルを直接操作して置き換えます。例のように、-i
オプションに追加の引数を指定すると、そこに指定した文字列をサフィックスとしたファイルにバックアップファイルを作成してから、元のファイルを操作して置き換えます。(訳注: -iオプションと引数文字列の間に空白を置いてはいけません。)
データの同一性を保証する
Linuxでファイルを操作することがいかに簡単であるかを示しました。次に、ファイルを他の人に配布する場合に、受け取った相手が元のファイルと同じであることを確認する方法を解説します。Linuxディストリビューションのダウンロード用CD/DVDイメージと共に、そのディスクイメージのハッシュ値を含むファイルをサーバーにホストする場合などに、この手法を使います。以下はDebianのダウンロードミラーからのリスト例です。
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ [PARENTDIR] Parent Directory - [SUM] MD5SUMS 2019-09-08 17:46 274 [CRT] MD5SUMS.sign 2019-09-08 17:52 833 [SUM] SHA1SUMS 2019-09-08 17:46 306 [CRT] SHA1SUMS.sign 2019-09-08 17:52 833 [SUM] SHA256SUMS 2019-09-08 17:46 402 [CRT] SHA256SUMS.sign 2019-09-08 17:52 833 [SUM] SHA512SUMS 2019-09-08 17:46 658 [CRT] SHA512SUMS.sign 2019-09-08 17:52 833 [ISO] debian-10.1.0-amd64-netinst.iso 2019-09-08 04:37 335M [ISO] debian-10.1.0-amd64-xfce-CD-1.iso 2019-09-08 04:38 641M [ISO] debian-edu-10.1.0-amd64-netinst.iso 2019-09-08 04:38 405M [ISO] debian-mac-10.1.0-amd64-netinst.iso 2019-09-08 04:38 334M ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
上記のリストでは、Debianのインストールイメージファイルに対応する、さまざまなアルゴリズム(MD5、SHA1、SHA256、SHA512)によるハッシュ値を格納したテキストファイルが付属しています。
Note
|
ハッシュ値とは、ファイルに対して暗号化ハッシュ関数に基づく計算を行って算出された値です。強度が異なるさまざまなタイプの暗号化ハッシュ関数があり、LPIC-1では |
ファイル(たとえば、debian-10.1.0-amd64-netinst.iso
イメージ)をダウンロードしたら、ダウンロードしたファイルのハッシュ値を計算し、提供されたハッシュ値と比較します。
次の例でポイントを説明しましょう。sha256sum
コマンドを使用して、ftu.txt
ファイルのSHA256ハッシュ値を計算します。
$ sha256sum ftu.txt 345452304fc26999a715652543c352e5fc7ee0c1b9deac6f57542ec91daf261c ftu.txt
ファイル名の前にある長い文字列が、このテキストファイルのSHA256ハッシュ値です。その値を含むファイルを作成して、ftu.txt
が変更されていないことを検証しましょう。sha256sum
コマンドの出力をファイルにリダイレクトして、ハッシュ値のファイルを作成します。
$ sha256sum ftu.txt > sha256.txt
次に、ftu.txt
ファイルが変更されていないことを確認するために、sha256sum
コマンドに -c
オプションとハッシュ値を含むファイル名を指定します。
$ sha256sum -c sha256.txt ftu.txt: OK
このように、ファイルに含まれるハッシュ値と、ftu.txt
ファイルに対して計算したSHA256ハッシュ値が一致していることが示されます。元のファイルが変更された場合(ファイルのダウンロード中に数バイトが失われた場合や、誰かがファイルを改ざんした場合など)は、値のチェックが失敗します。失敗した場合は、ファイルが不良または破損している可能性が高く、内容の同一性が損なわれています。確認するために、ftu.txt
の末尾にテキストを追加して試してみましょう。
$ echo "new entry" >> ftu.txt
再度、ファイルの同一性を検証してみます。
$ sha256sum -c sha256.txt ftu.txt: FAILED sha256sum: WARNING: 1 computed checksum did NOT match
計算した値と、ハッシュファイルの内容が異なってことがわかります。つまり、このファイルの内容が変更されたということです。ファイルの重要度に応じて、ファイルのコピーをもう一回ダウンロードしたり、チェックサムの不一致をファイルの送信者に連絡したり、データセンターのセキュリティチームに報告しましょう。
ファイルを詳しく調べる
od
(8進ダンプ)コマンドを使って、アプリケーションやさまざまなファイルをデバッグすることがよくあります。オプションを指定しない od
コマンドは、ファイルの内容を8進数でリストします。先程作成した ftu.txt
ファイルでやってみましょう。
$ od ftu.txt 0000000 075142 060543 005164 060543 005164 072543 005164 062550 0000020 062141 066012 071545 005163 062155 071465 066565 067012 0000040 005154 062157 070012 071541 062564 071412 062145 071412 0000060 060550 032462 071466 066565 071412 060550 030465 071462 0000100 066565 071412 071157 005164 070163 064554 005164 060564 0000120 066151 072012 005162 067165 070551 073412 005143 075170 0000140 060543 005164 061572 072141 000012 0000151
出力の最初の列は、ファイル先頭からの オフセット を8進数で示した数値です。od
はデフォルトで情報を8進数で出力しますから、各行はバイト単位のオフセット(8進数)で始まり、ファイル内容を2バイトずつ8進数で示した値が8つ続きます。つまり、1行には16バイト分の内容が表示されます。なお、2バイトの順序はCPUアーキテクチャによって異なっています。
Tip
|
1 バイト は8ビットです。 |
-x
オプションを指定すると、ファイルの内容が16進数で表示されます。(オフセットは8進数のままです)。
$ od -x ftu.txt 0000000 7a62 6163 0a74 6163 0a74 7563 0a74 6568 0000020 6461 6c0a 7365 0a73 646d 7335 6d75 6e0a 0000040 0a6c 646f 700a 7361 6574 730a 6465 730a 0000060 6168 3532 7336 6d75 730a 6168 3135 7332 0000100 6d75 730a 726f 0a74 7073 696c 0a74 6174 0000120 6c69 740a 0a72 6e75 7169 770a 0a63 7a78 0000140 6163 0a74 637a 7461 000a 0000151
このように、バイトオフセットの後の8列それぞれが、16進数で表示されます。
od
コマンドは、スクリプトのデバッグなどに便利です。例えば、od
コマンドは、ファイル内の 改行(newline)などの、通常は見えない文字を見えるようにします。-c
オプションを指定すると、数値(文字コード)ではなく、(エスケープされた)文字で表示します。
$ od -c ftu.txt 0000000 b z c a t \n c a t \n c u t \n h e 0000020 a d \n l e s s \n m d 5 s u m \n n 0000040 l \n o d \n p a s t e \n s e d \n s 0000060 h a 2 5 6 s u m \n s h a 5 1 2 s 0000100 u m \n s o r t \n s p l i t \n t a 0000120 i l \n t r \n u n i q \n w c \n x z 0000140 c a t \n z c a t \n 0000151
ファイル内の改行は、文字 \n
で示されます。ファイル内のすべての文字を表示するだけで、バイトオフセットを表示する必要がない場合は、次のようにバイトオフセット列を出力から除外できます。
$ od -An -c ftu.txt b z c a t \n c a t \n c u t \n h e a d \n l e s s \n m d 5 s u m \n n l \n o d \n p a s t e \n s e d \n s h a 2 5 6 s u m \n s h a 5 1 2 s u m \n s o r t \n s p l i t \n t a i l \n t r \n u n i q \n w c \n x z c a t \n z c a t \n
演習
-
学校にノートパソコンが寄付されたので、そのノートパソコンにLinuxをインストールしたいと思っています。OSが起動せずマニュアルがないため、LinuxをUSBブートしてノートパソコンのCPU情報を確認することにしました。シェルターミナルを起動して、/proc/cpuinfo ファイルの中身を確認しました:
processor : 0 vendor_id : GenuineIntel cpu family : 6 model : 158 (省略) processor : 1 vendor_id : GenuineIntel cpu family : 6 model : 158 (省略)
-
コマンド
grep
とwc
を使用して、ノートパソコンのプロセッサ数を表示してください。 -
grep
の代わりにsed
を使用して、同じ内容を表示してください。
-
-
コマンド
grep
、sed
、head
、tail
を使用して、/etc/passwd
ファイルから、指定された内容を表示してください。-
Bashシェルを利用するユーザをリストしてください。
-
システムには、プログラムを処理ないし管理するためのユーザが存在します。これらのユーザはシェルを使用しません。システムに存在する、そのようなユーザをカウントしてください。
-
/etc/passwd
ファイルのみを参照して、システムに存在するユーザーとグループの数を調べて下さい。 -
/etc/passwd
ファイルの、最初の行、最後の行、および10行目のみをリストしてください。
-
-
以下の
/etc/passwd
ファイルをこの問題の例として使います。コピーしてmypasswd
というファイルに保存して下さい。(訳注: 第5フィールド(GECOS)はメモで、このような内容であるとは限りません。)root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync nvidia-persistenced:x:121:128:NVIDIA Persistence Daemon,,,:/nonexistent:/sbin/nologin libvirt-qemu:x:64055:130:Libvirt Qemu,,,:/var/lib/libvirt:/usr/sbin/nologin libvirt-dnsmasq:x:122:133:Libvirt Dnsmasq,,,:/var/lib/libvirt/dnsmasq:/usr/sbin/nologin carol:x:1000:2000:Carol Smith,Finance,,,Main Office:/home/carol:/bin/bash dave:x:1001:1000:Dave Edwards,Finance,,,Main Office:/home/dave:/bin/ksh emma:x:1002:1000:Emma Jones,Finance,,,Main Office:/home/emma:/bin/bash frank:x:1003:1000:Frank Cassidy,Finance,,,Main Office:/home/frank:/bin/bash grace:x:1004:1000:Grace Kearns,Engineering,,,Main Office:/home/grace:/bin/ksh henry:x:1005:1000:Henry Adams,Sales,,,Main Office:/home/henry:/bin/bash john:x:1006:1000:John Chapel,Sales,,,Main Office:/home/john:/bin/bash
-
mypasswd
ファイルから、グループ1000
のユーザーをすべてリストして下さい。適当なフィールドを選択するために、sed
を使って下さい。 -
そのグループのユーザー全員の氏名をリストして下さい。
sed
とcut
を使います。
-
発展演習
-
前の演習の
mypasswd
ファイルをもう一度使用し、Main Officeから1人をくじ引きで選ぶコマンドラインを作成してください。sed
コマンドを使用してMain Officeの行のみを出力し、そこからcut
コマンドを使用してユーザーの名前を取り出します。次に、それらの名前をランダムに並べ替えて、リストから一番上の名前だけを出力します。 -
財務部門(Finance)、技術部門(Engineering)、営業部門(Sales)で働く人は、それぞれ何人ですか?
uniq
コマンドを使います。 -
次に、カンマ区切りのCSVファイルを用意して、LibreOffice に簡単にインポートできるようにします。先ほどの例で作った
mypasswd
ファイルからnames.csv
を作成してください。ファイルの内容は、以下のような形式とします。名,姓,所属 Carol,Smith,Finance ... John,Chapel,Sales
ヒント:
sed
、cut
、paste
コマンドを使用します。このファイルでは、区切り文字がコンマ(,
)になることに注意して下さい。 -
前の演習で作成した
names.csv
というスプレッドシートは重要なファイルですから、改ざんされていないことを確認できるようにします。md5sum
を使って、このファイルが変更されていないことを確認するにはどうしますか。 -
古典文学を毎日100行読むことを自分に課し、Herman Melvilleの Mariner and Mystic から読み始めることにしました。この本を100行ずつのセクションに分割する
split
コマンドはどうなりますか? なお、この本を入手するには、https://www.gutenberg.org で検索してください。 -
/etc
ディレクトリでls -l
を実行して得られたリストを例にします。ls
コマンドの出力結果から、cut
コマンドを使用して、ファイル名のみを表示してください。次に、ファイル名とファイルの所有者を表示して下さい。tr
コマンドで、連続する空白を1つの空白に まとめる ことができますから、その出力をcut
コマンドで出力します。 -
この演習では、(仮想マシンではなく)実機を使用していることと、USBメモリーを所持していることを前提とします。
tail
コマンドのマニュアルページを参照して、ファイルにテキストが追加されたことを追跡する方法を見つけてください。/var/log/syslog
ファイルへの出力をtail
コマンドで監視し、USBメモリーを挿入した時に、製品名、メーカー、容量を表示するコマンドはどうなりますか?
まとめ
Linuxシステム管理において、テキストストリームの処理は非常に重要です。スクリプトを使用してテキストストリームを処理し、日常のタスクを自動化したり、ログファイルから情報を検索することができます。このレッスンで取り上げたコマンドの概要は次のとおりです。
cat
-
プレーンテキストファイルを読み取る、ないしは、結合します。
bzcat
-
bzip2
形式を使用して圧縮されたファイルを読み取ります。 xzcat
-
xz
形式を使用して圧縮されたファイルを読み取ります。 zcat
-
gzip
形式を使用して圧縮されたファイルを読み取ります。 less
-
ファイルの内容をページングし、閲覧や検索を行います。
head
-
デフォルトではファイルの先頭10行を表示します。
-n
オプションを使用すると、表示する行数を増減できます。 tail
-
デフォルトではファイルの末尾10行を表示します。
-n
オプションを使用すると、表示する行数を増減できます。-f
オプションはテキストファイルを追跡して、新しいデータが書き込まれた時にそれらをすぐに出力します。 wc
-
“word count” の略で、文字、単語、行をカウントします。オプションでカウント対象を指定できます。
sort
-
行ごとに、アルファベット順、アルファベットの逆順、ランダムな順序に、並べ替えて出力します。
uniq
-
同一の行を1行にまとめて、まとめた行数と共に表示します。ファイルはソートされている必要があります。
od
-
“octal dump” に由来するコマンドで、ファイルの内容を8進数、10進数、16進数などで表示します。
nl
-
“number line” コマンドは、各行の先頭に行番号を付けてファイルを表示します。
sed
-
ストリームエディターは、正規表現を使用して一致する文字列を検索したり、ファイルを編集します。
tr
-
“translate” に由来するコマンドで、文字を置き換えたり、文字の繰り返しを削除ないしまとめます。
cut
-
区切り文字に基づいて行をフィールドに区切り、テキストファイルのカラム(列)を出力します。
paste
-
それぞれのファイルをカラムとして、区切り文字で区切りながら結合します。
split
-
オプションで指定した条件で、大きなファイルを小さなファイルに分割します。
md5sum
-
ファイルのMD5ハッシュ値を計算します。また、既存のハッシュ値とファイルの同一性を検証することもできます。
sha256sum
-
ファイルのSHA256ハッシュ値を計算します。また、既存のハッシュ値とファイルの同一性を検証することもできます。
sha512sum
-
ファイルのSHA512ハッシュ値を計算します。また、既存のハッシュ値とファイルの同一性を検証することもできます。
演習の解答
-
学校にノートパソコンが寄付されたので、そのノートパソコンにLinuxをインストールしたいと思っています。OSが起動せずマニュアルがないため、LinuxをUSBブートしてノートパソコンのCPU情報を確認することにしました。シェルターミナルを起動して、/proc/cpuinfo ファイルの中身を確認しました:
processor : 0 vendor_id : GenuineIntel cpu family : 6 model : 158 (省略) processor : 1 vendor_id : GenuineIntel cpu family : 6 model : 158 (省略)
-
コマンド grep と wc を使用して、ノートパソコンのプロセッサの数を表示してください。
2つの方法があります。
$ cat /proc/cpuinfo | grep processor | wc -l $ grep processor /proc/cpuinfo | wc -l
同じことを行う方法がいくつもありますが、どちらを使うのがよいでしょう。いくつかの要因がありますが、最も重要なのはパフォーマンスと読みやすさの2つです。タスクを自動化するためのシェルスクリプトで、コマンドを使用することが多いでしょう。スクリプトが大きく複雑になるほど、パフォーマンスに気を配る必要があります。
-
grep
の代わりにsed
で同じことを行ってみましょう。grep
の代わりにsed
を使います:$ sed -n /processor/p /proc/cpuinfo | wc -l
sed
に-n
オプションを指定したので、processor
に一致した行のみがp
コマンドで出力されます。そしてgrep
コマンドの場合と同様に、wc -l
で行数 〜 すなわちプロセッサの数をカウントします。別の例も見てみましょう:
$ sed -n /processor/p /proc/cpuinfo | sed -n '$='
このコマンドは、
sed
の出力をwc
コマンドにパイプした前の例と同じ結果を出力します。ここでの違いは、wc -l
で行数をカウントするのではなく、sed
を再度呼び出して同じことを行っています。ここでも-n
オプションを使用してsed
の出力を抑制して、今度は'$='
を指示しています。この式は、$
が最終行への一致を表し、=
がその行番号を出力することを指示しています。
-
-
コマンド
grep
、sed
、head
、tail
を使用して、/etc/passwd
ファイルから,指定された内容を表示してください。-
Bashシェルを利用するユーザをリストしてください。
$ grep ":/bin/bash$" /etc/passwd
Bashシェルを利用するユーザ名のみを表示するには、次のようにします。
$ grep ":/bin/bash$" /etc/passwd | cut -d: -f1
grep
コマンドの出力をcut
コマンドにパイプします。/etc/passwd
ファイルは区切り文字が:
なので、cut
コマンドに-d:
オプションを指定し、ユーザー名は1番目フィールドなので-f1
オプションを指定します。 -
システムには、特定のプログラムを処理ないし管理するためのユーザが存在します。これらのユーザはシェルを使用しません。システムに存在する、そのようなユーザをカウントしてください。
最も簡単な方法は、Bashシェルを使用しないアカウントの行を出力する方法です。
$ grep -v ":/bin/bash$" /etc/passwd | wc -l
-
/etc/passwd ファイルのみを参照して、システムに存在するユーザーとグループの数を調べて下さい。
/etc/passwd
の各行のフィールドは、最初がユーザ名、2番目は通常x
でユーザのパスワードが/etc/shadow
ファイルで暗号化されていることを示し、3番目はユーザID(UID)、4番目はグループID(GID)です。ここからユーザ数がわかります。$ cut -d: -f3 /etc/passwd | wc -l
ほとんどの場合はこれで大丈夫ですが、スーパーユーザーや特殊なユーザーが1つのUID(ユーザーID)を共有していることがあります。念のため、
cut
コマンドの結果をsort
コマンドにパイプしてから、行数をカウントします。(訳注:sort
の-u
オプションは、uniq
コマンドと同様に同一行をまとめます。)$ cut -d: -f3 /etc/passwd | sort -u | wc -l
グループ数も同様です。
$ cut -d: -f4 /etc/passwd | sort -u | wc -l
-
/etc/passwd
ファイルの、最初の行、最後の行、および10行目のみをリストしてください。次のようになります。
$ sed -n -e '1'p -e '10'p -e '$'p /etc/passwd
-n
はp
コマンドで指定されたものだけを表示するオプションです。ここで使われているドル記号($
)は、ファイルの最終行への一致を意味します。
-
-
以下の
/etc/passwd
ファイルをこの問題の例として使います。コピーしてmypasswd
というファイルに保存して下さい。(訳注: 実際には、第5フィールド(GECOS)はメモであり、必ずしもこのような内容であるとは限りません。)root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync nvidia-persistenced:x:121:128:NVIDIA Persistence Daemon,,,:/nonexistent:/sbin/nologin libvirt-qemu:x:64055:130:Libvirt Qemu,,,:/var/lib/libvirt:/usr/sbin/nologin libvirt-dnsmasq:x:122:133:Libvirt Dnsmasq,,,:/var/lib/libvirt/dnsmasq:/usr/sbin/nologin carol:x:1000:2000:Carol Smith,Finance,,,Main Office:/home/carol:/bin/bash dave:x:1001:1000:Dave Edwards,Finance,,,Main Office:/home/dave:/bin/ksh emma:x:1002:1000:Emma Jones,Finance,,,Main Office:/home/emma:/bin/bash frank:x:1003:1000:Frank Cassidy,Finance,,,Main Office:/home/frank:/bin/bash grace:x:1004:1000:Grace Kearns,Engineering,,,Main Office:/home/grace:/bin/ksh henry:x:1005:1000:Henry Adams,Sales,,,Main Office:/home/henry:/bin/bash john:x:1006:1000:John Chapel,Sales,,,Main Office:/home/john:/bin/bash
-
mypasswd
ファイルから、グループ1000
のユーザーをすべてリストして下さい。適当なフィールドを選択するために、sed
を使って下さい。/etc/passwd
ファイルの4番目のフィールドがGIDです。こうやりたくなるかもしれません:$ sed -n /1000/p mypasswd
残念ながら、この場合、次の行も含まれてしまいます:
carol:x:1000:2000:Carol Smith,Finance,,,Main Office:/home/carol:/bin/bash
Carol SmithはGID 2000のメンバーであり、UIDが条件に一致したものですから誤りです。このような場合は、例えばGIDの次のフィールドが大文字で始まっていることに着目しましょう。これに気づけば、この問題を解決するために正規表現を利用できます。
$ sed -n /:1000:[A-Z]/p mypasswd
[A-Z]
というパターンは、任意の大文字1文字にマッチします。正規表現については、レッスン103.7で詳しく学びます。 -
そのグループのユーザー全員の氏名をリストして下さい。sed と cut を使います。
2問目の最初の設問に対する解答と同様に、
cut
コマンドにパイプします。$ sed -n /:1000:[A-Z]/p mypasswd | cut -d: -f5 Dave Edwards,Finance,,,Main Office Emma Jones,Finance,,,Main Office Frank Cassidy,Finance,,,Main Office Grace Kearns,Engineering,,,Main Office Henry Adams,Sales,,,Main Office John Chapel,Sales,,,Main Office
まだ余計な情報が残っています。結果の中のフィールドは
,
で区切られていることに着目しましょう。今度は区切り文字を,
として、cut
コマンドにパイプします。$ sed -n /:1000:[A-Z]/p mypasswd | cut -d: -f5 | cut -d, -f1 Dave Edwards Emma Jones Frank Cassidy Grace Kearns Henry Adams John Chapel
-
発展演習の解答
-
前の演習の
mypasswd
ファイルをもう一度使用し、Main Officeから1人をくじ引きで選ぶコマンドラインを作成してください。sed
コマンドを使用してMain Officeの行のみを出力し、そこからcut
コマンドを使用してユーザーの名前を取り出します。次に、これらの名前をランダムに並べ替えて、リストから一番上の名前だけを出力します。sort
コマンドの-R
オプションの動作を調べておきましょう。次のコマンドを数回繰り返してみてください(sed
が一つの文字列として扱うように、'Main Office' とシングルクォートで囲むことに注意してください。):$ sed -n /'Main Office'/p mypasswd | cut -d: -f5 | cut -d, -f1 | sort -R
ランダムに並び換わることが確認できたでしょうか。最後に、リストの先頭を出力します:
$ sed -n /'Main Office'/p mypasswd | cut -d: -f5 | cut -d, -f1 | sort -R | head -1
-
財務部門(Finance)、技術部門(Engineering)、営業部門(Sales)で働く人は、それぞれ何人ですか?
uniq
コマンドを使います。今までの演習で学んだことを応用すれば、回答することができます。次のようにやってみてください:
$ sed -n /'Main Office'/p mypasswd $ sed -n /'Main Office'/p mypasswd | cut -d, -f2
ここでは、区切り文字が
:
であることを気にする必要はありません。今回は行を文字,
で分割して、その、2番目のフィールドを出力すればよいのです。$ sed -n /'Main Office'/p mypasswd | cut -d, -f2 | uniq -c 4 Finance 1 Engineering 2 Sales
uniq
コマンドは、重複する行を省いて、1行だけを出力します。-c
オプションは、重複行の出現回数を数えることを指示します。uniq
は連続する重複行のみを処理しますから、重複行が離れている場合にはまずsort
コマンドを使用する必要があります。 -
次に、カンマ区切りのCSVファイルを用意して、LibreOffice に簡単にインポートできるようにします。先ほどの例で作った
mypasswd
ファイルからnames.csv
を作成してください。ファイルの内容は、以下のような形式とします。名,姓,所属 Carol,Smith,Finance ... John,Chapel,Sales
ヒント:
sed
、cut
、paste
コマンドを使用します。このファイルでは、区切り文字がコンマ(,
)になることに注意して下さい。ここまでの演習で学んだことを踏まえて、まずは
sed
とcut
コマンドから始ましょう:$ sed -n /'Main Office'/p mypasswd | cut -d: -f5 | cut -d" " -f1 > firstname
これで、従業員の名前を含む
firstname
ファイルができました。$ sed -n /'Main Office'/p mypasswd | cut -d: -f5 | cut -d" " -f2 | cut -d, -f1 > lastname
これで、各従業員の姓を含む
lastname
ファイルができました。最後に、各従業員の所属部署を含むファイルを作成します:
$ sed -n /'Main Office'/p mypasswd | cut -d: -f5 | cut -d, -f2 > department
先に進む前に、コマンドを試して出力を確認しましょう:
$ cat firstname lastname department $ paste firstname lastname department
最後の仕上げです:
$ paste firstname lastname department | tr '\t' , $ paste firstname lastname department | tr '\t' , > names.csv
ここでは、
tr
コマンドで、区切り文字をタブを意味する\t
から,
に変換しています。tr
は、ある文字を別の文字に置き換えるときに非常に便利です。tr
とpaste
のmanページを読んでみてください。例えば、paste
の-d
オプションで区切り文字を指定すると、前のコマンドをよりシンプルにすることができます。$ paste -d, firstname lastname department
ここでは、コマンドを紹介するために
paste
コマンドを使いましたが、すべてのタスクを1行で簡単に実行することもできます:$ sed -n /'Main Office'/p mypasswd | cut -d: -f5 | cut -d, -f1,2 | tr ' ' , > names.csv
-
前の演習で作成した
names.csv
というスプレッドシートは重要なファイルですから、改ざんされていないことを確認できるようにします。md5sum
を使って、このファイルの同一性を確認するにはどうしますか。md5sum
、sha256sum
、sha512sum
のマニュアルページを見ると、どれにも以下の文言が含まれていることが分かります:“compute and check XXX message digest” (メッセージダイジェストの計算と照合を行う)
ここでの “XXX” は、メッセージダイジェスト を計算するために使用するアルゴリズムです。
ここでは例として
md5sum
を使用しますが、他のコマンドも試してください。$ md5sum names.csv 61f0251fcab61d9575b1d0cbf0195e25 names.csv
例えば、sftpサービスなどにファイルをアップロードし、別の安全な方法で メッセージダイジェスト(ハッシュ値) を送信します。もしファイルが少しでも違っていたら、メッセージダイジェスト は全く異なるものになるでしょう。確認するために
names.csv
を編集して、JonesをJamesに変更してみます:$ sed -i.backup s/Jones/James/ names.csv $ md5sum names.csv f44a0d68cb480466099021bf6d6d2e65 names.csv
ファイルをダウンロードさせるときは、常に対応する メッセージダイジェスト を添えて配布するのがお勧めです。そうすれば、ダウンロードした人が メッセージダイジェスト を計算して、オリジナルと照合することができます。例えば、https://kernel.org には、ダウンロード可能なすべてのファイルのsha256sum値を取得できるページ https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/sha256sums.asc が用意されています。
-
古典文学を毎日100行読むことを自分に課し、Herman Melvilleの Mariner and Mystic から読み始めることにしました。この本を100行ずつのセクションに分割する
split
コマンドはどうなりますか? なお、この本を入手するには、https://www.gutenberg.org で検索してください。まず、Gutenbergプロジェクトのサイトから本を入手します。このサイトでは、この本だけでなく、パブリックドメインで入手可能な他の本も入手できます。
$ wget https://www.gutenberg.org/files/50461/50461-0.txt
wget
がシステムにインストールされていない場合は、インストールします。あるいは、curl
コマンドを使用することもできます。ファイルを入手したら、less
を使用して確認します。$ less 50461-0.txt
次に、この本を100行ごとの塊に分割します。
$ split -l 100 -d 50461-0.txt melville
50461-0.txt
はこれから分割するファイルの名前です。melville
は分割後のファイル名のプレフィックスになります。-l 100
は分割する行数を示し、-d
オプションはファイル名のサフィックスを(英字ではなく)数値にすることでファイル名に番号を振るように指定します。分割されたファイルのいずれか(最後のファイル以外)でnl
を使用して、それぞれが100行であることを確認してみましょう。 -
/etc
ディレクトリでls -l
を実行して得られたリストを例にします。ls
コマンドの出力結果から、cut
コマンドを使用して、ファイル名のみを表示してください。次に、ファイル名とファイルの所有者を表示して下さい。tr
コマンドで、連続する空白を1つの空白に まとめる ことができますから、その出力をcut
コマンドで出力します。オプションを指定しない
ls
コマンドは、ファイルの名前だけを表示します。ls -l
(長いリスト)では、より詳細な情報を表示できます。$ ls -l /etc | tr -s ' ' , drwxr-xr-x,3,root,root,4096,out,24,16:58,acpi -rw-r--r--,1,root,root,3028,dez,17,2018,adduser.conf -rw-r--r--,1,root,root,10,out,2,17:38,adjtime drwxr-xr-x,2,root,root,12288,out,31,09:40,alternatives -rw-r--r--,1,root,root,401,mai,29,2017,anacrontab -rw-r--r--,1,root,root,433,out,1,2017,apg.conf drwxr-xr-x,6,root,root,4096,dez,17,2018,apm drwxr-xr-x,3,root,root,4096,out,24,16:58,apparmor drwxr-xr-x,9,root,root,4096,nov,6,20:20,apparmor.d
tr
の-s
オプションは、指定した文字が連続している場合に、それら1つにまとめることを指示します。ここでは空白を指定しているので、連続する空白を1つにまとめます。tr
コマンドでは、指定したすべての文字の繰り返しが対象になります。この例ではさらに、空白をコンマ,
に置き換えていますが、題意からは空白のままで構わないので、,
を省略します。$ ls -l /etc | tr -s ' ' drwxr-xr-x 3 root root 4096 out 24 16:58 acpi -rw-r--r-- 1 root root 3028 dez 17 2018 adduser.conf -rw-r--r-- 1 root root 10 out 2 17:38 adjtime drwxr-xr-x 2 root root 12288 out 31 09:40 alternatives -rw-r--r-- 1 root root 401 mai 29 2017 anacrontab -rw-r--r-- 1 root root 433 out 1 2017 apg.conf drwxr-xr-x 6 root root 4096 dez 17 2018 apm drwxr-xr-x 3 root root 4096 out 24 16:58 apparmor
ファイル名だけを表示するには、9番目のフィールドのみを表示します。
$ ls -l /etc | tr -s ' ' | cut -d" " -f9
ファイル名とファイルの所有者を表示する場合は、9番目と3番目のフィールドを使います。
$ ls -l /etc | tr -s ' ' | cut -d" " -f9,3
ちなみに、フォルダ名とその所有者だけが必要な場合は、以下のようにします。
$ ls -l /etc | grep ^d | tr -s ' ' | cut -d" " -f9,3
-
この演習では、(仮想マシンではなく)実機を使用していることと、USBメモリーを所持していることを前提とします。
tail
コマンドのマニュアルページを参照して、ファイルにテキストが追加されたことを追跡する方法を見つけてください。/var/log/syslog
ファイルへの出力をtail
コマンドで監視し、USBメモリーを挿入した時に、製品名、メーカー、容量を表示するコマンドはどうなりますか?$ tail -f /var/log/syslog | grep -E -i 'product\:\|blocks\|manufacturer' Nov 8 06:01:35 brod-avell kernel: [124954.369361] usb 1-4.3: Product: Cruzer Blade Nov 8 06:01:35 brod-avell kernel: [124954.369364] usb 1-4.3: Manufacturer: SanDisk Nov 8 06:01:37 brod-avell kernel: [124955.419267] sd 2:0:0:0: [sdc] 61056064 512-byte logical blocks: (31.3 GB/29.1 GiB)
これは一例です。表示される結果はUSBメモリーの製造元によって異なります。検索対象となる文字列が大文字か小文字かわからないので、
grep
コマンドに-i
オプションを使用しています。また、|
はOR検索を指示していて、つまりproduct
、blocks
、manufacturer
のいずれかを含む行を検索します。また、|
は拡張正規表現のパターンなので、-E
オプションを指定します。