わさっきhb

大学(教育研究)とか ,親馬鹿とか,和歌山とか,とか,とか.

test(?s,ファイル名)でファイルサイズが得られない?

 いきなりですが問題です.

1. Linuxのシェルで,指定したファイル名のファイルサイズだけを出力するコマンドを答えなさい.
2. Rubyで,指定したファイル名のファイルサイズだけを出力するコードを答えなさい.
いずれも,ファイルサイズはバイト数で10進数値とします.

 プログラミング科目の採点をしていたときの出来事です.ファイル振り分けプログラムを実行し,「学生番号-指定のファイル名.指定の拡張子」のファイルが多数,ある状況で,概観をつかむ*1ため,それぞれの(採点用テキストファイルの)ファイル名の後ろに,「 (ファイルサイズ)」をつけようと考えました.
 といったところで解答例です.Linuxでのコマンドは,次のとおりです.*2

ls -l ファイル名 | cut -f 5 -d ' '

 lsコマンドに,-lオプションとファイル名を指定すると(そしてそのファイルが存在すれば),出力は1行だけで,ファイルの詳細情報を出力してくれます.先頭から順に,パーミッション,ハードリンク数,オーナー名,グループ名,そしてファイルサイズ(バイトサイズ)です*3.それぞれは空白文字で区切られており,5番目ということで,cutコマンドを使用して取り出せば,おしまいというわけです*4
 つづきましてRubyのコードです.まっさきに思い浮かぶのは,test関数です.

puts test(?s, ファイル名)

 上記のとおり,「 (ファイルサイズ)」をつけるために,この処理(putsではありませんが)をすると,「 ()」という出力を見ることになりました.
 該当するファイルを,lsコマンドで確認すると,0バイトです.
 testメソッドで?sを指定したとき,空のファイルは,「0」になってくれないということでしょうか…
 Rubyのリファレンスマニュアルに,明記されていました.

?s
ファイルサイズが 0 でない (ファイルサイズを返す、0 ならば nil) -> Integer|nil

Kernel.#test (Ruby 2.7.0 リファレンスマニュアル)

 この挙動は,FileTestモジュールに由来することも,分かりました.module FileTestを見ると,testメソッドの第1引数に応じた,「?」で終わる(trueまたはfalseを返す)メソッドが,いくつもあります.そこに「size」と「size?」のメソッドもあり,前者の戻り値はInteger,後者は「Integer | nil」となっているのでした.
 ということで,Rubyのコードとして,ファイルサイズが0のファイルに「0」を出力するようにするには,「puts FileTest.size(ファイル名)」と書けばよい,となります.
 動作確認のコマンドです.

$ touch emptyfile
$ ls -l emptyfile
-rw-r--r-- 1 takehikom takehikom 0  8月 23 06:00 emptyfile
$ ls -l emptyfile | cut -f 5 -d ' '
0
$ ruby -e 'puts FileTest.size("emptyfile")'
0
$ ruby -e 'puts FileTest.size?("emptyfile")'

$ ruby -e 'puts test(?s,"emptyfile")'

$ echo -n '1' > onefile
$ ls -l onefile
-rw-r--r-- 1 takehikom takehikom 0  8月 23 06:01 onefile
$ ls -l onefile | cut -f 5 -d ' '
1
$ ruby -e 'puts FileTest.size("onefile")'
1
$ ruby -e 'puts FileTest.size?("onefile")'
1
$ ruby -e 'puts test(?s,"onefile")'
1
$ rm emptyfile onefile

*1:「ファイルサイズが大きいものを見つける」ではありません.むしろ,ファイルサイズの小さいもののほうが,良くない答案のことが多いです.それと,ダウンロード・編集・提出という課題のとき,もとのとファイルサイズが同じということもありまして,これも概観チェックで行うことの一つです.

*2:lsもcutも,Ubuntuではcoreutilsパッケージに入っています.dpkg -L coreutilsを実行したところ,出力に「/bin/ls」と「/usr/bin/cut」が含まれていました.ディレクトリが異なるのですね.

*3:https://eng-entrance.com/linux_command_ls

*4:ファイル区切り文字について,「-d ' '」で空白1文字と指定しています.もし,ls -lの出力で,空白が複数入る場合には,cutの前にsedのコマンドを入れて,空白1文字にしておくとよいでしょう.コマンドは「ls -l ファイル名 | sed -e 's/ \+/ /g' | cut -f 5 -d ' '」になります.