練習するときは他でよく使うツールを模倣することにしている。
cloudshell を使うと環境準備が手軽。
#!/bin/python
import sys
import os
opts = []
files = []
# 実行前処理
for _ in sys.argv:
arg = _.strip()
if arg.startswith("-"):
opts.append(arg)
else:
files.append(arg)
self = files.pop(0)
# 実行
for file in files:
if(os.path.isfile(file)):
with open(file) as f:
for line in f:
print(line, end="")
# 実行権限の付与
chmod +x ~/bin/cat.py
# お試し
cat.py /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:65534:65534:Kernel Overflow User:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/usr/sbin/nologin
systemd-oom:x:998:998:systemd Userspace OOM Killer:/:/usr/sbin/nologin
systemd-resolve:x:193:193:systemd Resolver:/:/usr/sbin/nologin
redis6:x:997:996:Redis Database Server:/var/lib/redis6:/sbin/nologin
cloudshell-user:x:1000:995::/home/cloudshell-user:/bin/bash
使えそうな気がする。
関数の使い方を確認。
#!/bin/python
import sys
import os
# 関数
def subroutine(file):
if not os.path.isfile(file):
return
with open(file) as f:
for line in f:
print(line,end="")
# 実行前処理
opts = []
files = []
for _ in sys.argv:
arg = _.strip()
if arg.startswith("-"):
opts.append(arg)
else:
files.append(arg)
self = files.pop(0)
# 実行
for file in files:
subroutine(file)
early return も入れてネストが浅くなったので、良くなった気がする。
標準入力の受け取り方を確認。
#!/bin/python
import sys
import os
# 関数
def subroutine(stream):
for line in stream:
print(line,end="")
def filessubroutine(files):
for file in files:
if not os.path.isfile(file):
continue
with open(file) as f:
subroutine(f)
# 実行前処理
opts = []
files = []
for _ in sys.argv:
arg = _.strip()
if arg.startswith("-"):
opts.append(arg)
else:
files.append(arg)
self = files.pop(0)
# 実行
if len(files) == 0:
subroutine(sys.stdin)
else:
filessubroutine(files)
for file in files: が関数の中に押し込まれて、構造が変わった気がするけどまあヨシ。
cat.py /etc/passwd | cat.py
引数の - は標準入力を表すので、files に入れておいて sys.stdin として扱うように。
# ...
def filessubroutine(files):
for file in files:
if file == "-":
subroutine(sys.stdin)
elif os.path.isfile(file):
with open(file) as f:
subroutine(f)
# ...
opts = {}
for _ in sys.argv:
arg = _.strip()
if not arg == "-" and arg.startswith("-"):
opts[arg] = True
else:
files.append(arg)
# ...
man cat によると -T, --show-tabs はタブを ^I で表示するオプションらしい。
#!/bin/python
import sys
import os
# 関数
def linesubroutine(line):
for ch in line:
if opts.get("-T") or opts.get("--show-tabs"):
if ch == "\t":
ch = "^I"
print(ch, end="")
def subroutine(stream):
for line in stream:
linesubroutine(line)
def filessubroutine(files):
for file in files:
if file == "-":
subroutine(sys.stdin)
elif os.path.isfile(file):
with open(file) as f:
subroutine(f)
# 実行前処理
opts = {}
files = []
for _ in sys.argv:
arg = _.strip()
if not arg == "-" and arg.startswith("-"):
opts[arg] = True
else:
files.append(arg)
self = files.pop(0)
# 実行
if len(files) == 0:
subroutine(sys.stdin)
else:
filessubroutine(files)
汚くなってきたけど出来た気がする。
ちょうど /etc/csh.cshrc にタブが混ざっていたので、実行してみると cat と同じ結果が得られた。
cat.py /etc/csh.cshrc -T | grep '\^I'
同じく-E, --show-endsで行末を $ で表示するオプションらしいので追加。同じ。
def linesubroutine(line):
for ch in line:
if opts.get("-T") or opts.get("--show-tabs"):
if ch == "\t":
ch = "^I"
if opts.get("-E") or opts.get("--show-ends"):
if ch == "\n":
ch = "$\n"
print(ch, end="")
-n, --number は行番号を表示するとのこと。global の使い所がよくわからず。
# ...
linenum = 0
def linesubroutine(line):
if opts.get("-n") or opts.get("--number"):
global linenum
linenum += 1
num = str(linenum).rjust(6)
print(num + " ", end="")
#...
-s, --squeeze は連続する空白行を省略する。cat ではわりとよく使う。
# ...
def linesubroutine(line):
if opts.get("-s") or opts.get("--squeeze-blank"):
global lastline
if lastline == "\n" and line == "\n":
return
else:
lastline = line
# ...
満足してきたので一旦おわり。
#!/bin/python
import sys
import os
# 関数
def linesubroutine(line):
if opts.get("-s") or opts.get("--squeeze-blank"):
global lastline
if lastline == "\n" and line == "\n":
return
else:
lastline = line
if opts.get("-n") or opts.get("--number"):
global linenum
linenum += 1
num = str(linenum).rjust(6)
print(num + " ", end="")
for ch in line:
if opts.get("-T") or opts.get("--show-tabs"):
if ch == "\t":
ch = "^I"
if opts.get("-E") or opts.get("--show-ends"):
if ch == "\n":
ch = "$\n"
print(ch, end="")
def subroutine(stream):
for line in stream:
linesubroutine(line)
def filessubroutine(files):
for file in files:
if file == "-":
subroutine(sys.stdin)
elif os.path.isfile(file):
with open(file) as f:
subroutine(f)
# 実行前処理
opts = {}
files = []
linenum = 0
lastline = ""
for _ in sys.argv:
arg = _.strip()
if not arg == "-" and arg.startswith("-"):
opts[arg] = True
else:
files.append(arg)
self = files.pop(0)
# 実行
if len(files) == 0:
subroutine(sys.stdin)
else:
filessubroutine(files)
python にありがちな __name__ は出番が無かった。保守性とかお作法関連の機能だと思う。