※当サイトの記事には、広告・プロモーションが含まれます。

Linuxのcolumnコマンドの表形式の出力が崩れるので、awkで表形式っぽい出力を実現した方が良さ気

www.itmedia.co.jp

うん。他の手口もたくさん考えられるから、こういう機能を作るときは、アップロードする必要のあるファイル形式を洗い出して、それ以外は登録できないようにするのがいいね。今なら画像ファイル(PNGJPEGなど)やPDFファイルくらいに絞っておくのがいいかな。

え? PHPファイルを登録できるのはまずいでしょ…… Webアプリの「アップロード機能」に潜む“あるある”ワナ(1/3 ページ) - ITmedia NEWS

⇧ フロントエンド側とサーバサイド側でファイル形式をチェックするということだと思うけど、具体的な対策を端折るから、知見の無い人が良く分からないまま実装して脆弱性を改善できないってことになると思うんだけど...

啓蒙していただけるのはありがたいけど、具体的な対策の説明も端折らないで欲しい...

Linuxのcolumnコマンドの表形式の出力が崩れるので、awkで表形式っぽい出力を実現した方が良さ気

前に、

ts0818.hatenablog.com

⇧ 上記の記事で、cloumnコマンドの存在を知って使ってみたのだけど、列(カラム)の値の幅がバラバラの場合に崩れてしまうことが分かったので、何か方法が無いか探していたのですが、

stackoverflow.com

qiita.com

⇧ どうやら、awkの出力の際のフォーマット指定で、列(カラム)の桁数を決めることができるようなので、awkを使った方が良さそう。

そのために、事前に、列(カラム)の最大の桁数を何桁にするかは決めなければならないんだけど、少なくともcolumnコマンドのような表示崩れの心配は無くなりそう。

ただ、

unix.stackexchange.com

⇧ columnコマンドでもオプションを上手く活用すれば、崩れることは無いんかな?

と思ったけど、

stackoverflow.com

⇧ oオプションが、単なる区切り文字の指定っぽいので、やはり、columnコマンドだと表示崩れを回避でき無さそう...

ヘッダー行の列(カラム)の数と、ボディー行の列(カラム)の要素数が同じであれば、awkの出力の際のフォーマットで調整するのが良さそうですね。

試してみました。

■最大桁数を超えない場合

#!/usr/bin/env bash

HEADER=result-July,result-August,result-September

BODY_2020=230000,228000,210000
BODY_2021=214000,nothing,nothing
BODY_2022=nothing,0,nothing
BODY_2023=0,0,nothing
BODY_2024=202400000000000,202400,202400000

MAX_WIDTH=16
LF=$'\n'

# columnを使う方法
echo -e "${HEADER}${LF}${BODY_2020}" | column -s, -t > tmp.txt

head -n 1 tmp.txt > result.txt
tail -n 1 tmp.txt >> result.txt

echo "[BODY_2020]"
cat result.txt

cat /dev/null > tmp.txt

echo -e "${HEADER}${LF}${BODY_2021}" | column -s, -t > tmp.txt

head -n 1 tmp.txt > result.tmp.txt
tail -n +2 result.txt >> result.tmp.txt
tail -n 1 tmp.txt >> result.tmp.txt

echo "[BODY_2021]"
cat result.tmp.txt

cat /dev/null > result.txt
cat result.tmp.txt > result.txt
cat /dev/null > result.tmp.txt
cat /dev/null > tmp.txt

echo -e "${HEADER}${LF}${BODY_2022}" | column -s, -t > tmp.txt

head -n 1 tmp.txt > result.tmp.txt
tail -n +2 result.txt >> result.tmp.txt
tail -n 1 tmp.txt >> result.tmp.txt

echo "[BODY_2022]"
cat result.tmp.txt

cat /dev/null > result.txt
cat result.tmp.txt > result.txt
cat /dev/null > result.tmp.txt
cat /dev/null > tmp.txt

echo -e "${HEADER}${LF}${BODY_2023}" | column -s, -t > tmp.txt

head -n 1 tmp.txt > result.tmp.txt
tail -n +2 result.txt >> result.tmp.txt
tail -n 1 tmp.txt >> result.tmp.txt

echo "[BODY_2023]"
cat result.tmp.txt

cat /dev/null > result.txt
cat result.tmp.txt > result.txt
cat /dev/null > result.tmp.txt
cat /dev/null > tmp.txt

echo -e "${HEADER}${LF}${BODY_2024}" | column -s, -t > tmp.txt

head -n 1 tmp.txt > result.tmp.txt
tail -n +2 result.txt >> result.tmp.txt
tail -n 1 tmp.txt >> result.tmp.txt

echo "[BODY_2024]"
cat result.tmp.txt

cat /dev/null > result.txt
cat result.tmp.txt > result.txt
cat /dev/null > result.tmp.txt
cat /dev/null > tmp.txt

# 結果表示
echo "[column command result]"
cat result.txt

# awkを使う方法
echo "${HEADER}" > tmp.txt
echo "${BODY_2020}" >> tmp.txt
echo "${BODY_2021}" >> tmp.txt
echo "${BODY_2022}" >> tmp.txt
echo "${BODY_2023}" >> tmp.txt
echo "${BODY_2024}" >> tmp.txt

echo "[awk result]"
cat "tmp.txt" | awk -v max_width="${MAX_WIDTH}" -F',' '{ for(i=1;i<=NF;i++){ if(i==NF){ printf "%-"max_width"s\n",$i; }else{ printf "%-"max_width"s",$i; }}}' | tee -a result_awk.txt

■最大桁数を超えた場合

#!/usr/bin/env bash

HEADER=result-July,result-August,result-September

BODY_2020=230000,228000,210000
BODY_2021=214000,nothing,nothing
BODY_2022=nothing,0,nothing
BODY_2023=0,0,nothing
#BODY_2024=202400000000000,202400,202400000
BODY_2024=20240000000000000,202400,202400000

MAX_WIDTH=16
LF=$'\n'

# columnを使う方法
echo -e "${HEADER}${LF}${BODY_2020}" | column -s, -t > tmp.txt

head -n 1 tmp.txt > result.txt
tail -n 1 tmp.txt >> result.txt

echo "[BODY_2020]"
cat result.txt

cat /dev/null > tmp.txt

echo -e "${HEADER}${LF}${BODY_2021}" | column -s, -t > tmp.txt

head -n 1 tmp.txt > result.tmp.txt
tail -n +2 result.txt >> result.tmp.txt
tail -n 1 tmp.txt >> result.tmp.txt

echo "[BODY_2021]"
cat result.tmp.txt

cat /dev/null > result.txt
cat result.tmp.txt > result.txt
cat /dev/null > result.tmp.txt
cat /dev/null > tmp.txt

echo -e "${HEADER}${LF}${BODY_2022}" | column -s, -t > tmp.txt

head -n 1 tmp.txt > result.tmp.txt
tail -n +2 result.txt >> result.tmp.txt
tail -n 1 tmp.txt >> result.tmp.txt

echo "[BODY_2022]"
cat result.tmp.txt

cat /dev/null > result.txt
cat result.tmp.txt > result.txt
cat /dev/null > result.tmp.txt
cat /dev/null > tmp.txt

echo -e "${HEADER}${LF}${BODY_2023}" | column -s, -t > tmp.txt

head -n 1 tmp.txt > result.tmp.txt
tail -n +2 result.txt >> result.tmp.txt
tail -n 1 tmp.txt >> result.tmp.txt

echo "[BODY_2023]"
cat result.tmp.txt

cat /dev/null > result.txt
cat result.tmp.txt > result.txt
cat /dev/null > result.tmp.txt
cat /dev/null > tmp.txt

echo -e "${HEADER}${LF}${BODY_2024}" | column -s, -t > tmp.txt

head -n 1 tmp.txt > result.tmp.txt
tail -n +2 result.txt >> result.tmp.txt
tail -n 1 tmp.txt >> result.tmp.txt

echo "[BODY_2024]"
cat result.tmp.txt

cat /dev/null > result.txt
cat result.tmp.txt > result.txt
cat /dev/null > result.tmp.txt
cat /dev/null > tmp.txt

# 結果表示
echo "[column command result]"
cat result.txt

# awkを使う方法
echo "${HEADER}" > tmp.txt
echo "${BODY_2020}" >> tmp.txt
echo "${BODY_2021}" >> tmp.txt
echo "${BODY_2022}" >> tmp.txt
echo "${BODY_2023}" >> tmp.txt
echo "${BODY_2024}" >> tmp.txt

echo "[awk result]"
cat "tmp.txt" | awk -v max_width="${MAX_WIDTH}" -F',' '{ for(i=1;i<=NF;i++){ if(i==NF){ printf "%-"max_width"s\n",$i; }else{ printf "%-"max_width"s",$i; }}}' | tee -a result_awk.txt

⇧ という感じで、awkであれば、最大桁数を超えない限りでは表示の崩れを防ぐことができそう。(上記は、最大桁数を超えているので、awkでも表示崩れが起きている)

columnコマンドは、桁数の大きい列(カラム)の横幅を基準にしてしまうっぽいので表示崩れが起こってしまうようですね...

悲報...

man7.org

stackoverflow.com

⇧ 列(カラム)の横幅を指定する機能が、そもそもcolumnには存在しないっぽい...

columnコマンドの-cオプションを試してみましたが、

#!/usr/bin/env bash

HEADER=result-July,result-August,result-September

BODY_2020=230000,228000,210000
BODY_2021=214000,nothing,nothing
BODY_2022=nothing,0,nothing
BODY_2023=0,0,nothing
BODY_2024=202400000000000,202400,202400000
#BODY_2024=20240000000000000,202400,202400000

COLUMN_NUM=$(echo "${HEADER}" | awk -F ',' '{print NF}')
MAX_WIDTH=16
TOTAL_COLUMN_WIDTH=$((COLUMN_NUM*MAX_WIDTH))
LF=$'\n'

echo "${TOTAL_COLUMN_WIDTH}"

# columnを使う方法
echo -e "${HEADER}${LF}${BODY_2020}" | column -c "${TOTAL_COLUMN_WIDTH}" -s, -t > tmp.txt

head -n 1 tmp.txt > result.txt
tail -n 1 tmp.txt >> result.txt

echo "[BODY_2020]"
cat result.txt

cat /dev/null > tmp.txt

echo -e "${HEADER}${LF}${BODY_2021}" | column -c "${TOTAL_COLUMN_WIDTH}" -s, -t > tmp.txt

head -n 1 tmp.txt > result.tmp.txt
tail -n +2 result.txt >> result.tmp.txt
tail -n 1 tmp.txt >> result.tmp.txt

echo "[BODY_2021]"
cat result.tmp.txt

cat /dev/null > result.txt
cat result.tmp.txt > result.txt
cat /dev/null > result.tmp.txt
cat /dev/null > tmp.txt

echo -e "${HEADER}${LF}${BODY_2022}" | column -c "${TOTAL_COLUMN_WIDTH}" -s, -t > tmp.txt

head -n 1 tmp.txt > result.tmp.txt
tail -n +2 result.txt >> result.tmp.txt
tail -n 1 tmp.txt >> result.tmp.txt

echo "[BODY_2022]"
cat result.tmp.txt

cat /dev/null > result.txt
cat result.tmp.txt > result.txt
cat /dev/null > result.tmp.txt
cat /dev/null > tmp.txt

echo -e "${HEADER}${LF}${BODY_2023}" | column -c "${TOTAL_COLUMN_WIDTH}" -s, -t > tmp.txt

head -n 1 tmp.txt > result.tmp.txt
tail -n +2 result.txt >> result.tmp.txt
tail -n 1 tmp.txt >> result.tmp.txt

echo "[BODY_2023]"
cat result.tmp.txt

cat /dev/null > result.txt
cat result.tmp.txt > result.txt
cat /dev/null > result.tmp.txt
cat /dev/null > tmp.txt

echo -e "${HEADER}${LF}${BODY_2024}" | column -c "${TOTAL_COLUMN_WIDTH}" -s, -t > tmp.txt

head -n 1 tmp.txt > result.tmp.txt
tail -n +2 result.txt >> result.tmp.txt
tail -n 1 tmp.txt >> result.tmp.txt

echo "[BODY_2024]"
cat result.tmp.txt

cat /dev/null > result.txt
cat result.tmp.txt > result.txt
cat /dev/null > result.tmp.txt
cat /dev/null > tmp.txt

# 結果表示
echo "[column command result]"
cat result.txt

# awkを使う方法
echo "${HEADER}" > tmp.txt
echo "${BODY_2020}" >> tmp.txt
echo "${BODY_2021}" >> tmp.txt
echo "${BODY_2022}" >> tmp.txt
echo "${BODY_2023}" >> tmp.txt
echo "${BODY_2024}" >> tmp.txt

echo "[awk result]"
cat "tmp.txt" | awk -v max_width="${MAX_WIDTH}" -F',' '{ for(i=1;i<=NF;i++){ if(i==NF){ printf "%-"max_width"s\n",$i; }else{ printf "%-"max_width"s",$i; }}}' | tee -a result_awk.txt

⇧ 列(カラム)毎の横幅を固定できないから、盛大に表示崩れを起こしますな...

分かったことは、columnコマンドは実用に耐えないってことになりそうね...ネットの情報を鵜呑みにしてはいかんということですな...

どうしても、columnで頑張る場合は、ヘッダー行の列(カラム)の末尾に空白でパディングして桁数を増やすとかですかね、実現可能か分からないですが...

とりあえず、awkで最大桁数を決め打ちして、横幅を固定するしか今のところ上手い方法が思いつかない...

HTML、CSSJavaScriptなどを駆使すれば、レスポンシブデザインの可能なWebの世界と比較してしまうと、シェルスクリプトの限界を感じますな...

まぁ、そもそも、表形式ってレスポンシブデザインと相性悪いところありますから何とも言えないですけど。

横長のテーブルを可変にするにも限界ありまし、縦に並べるにしても、列(カラム)数が多いと、見辛くなるのは回避できそうにないですし...

毎度モヤモヤ感が半端ない...

今回はこのへんで。