在程式語言基礎上的 linux shell 指令教學

shell 的設計理念以 unix 環境中的程序為中心, 發展出參數、管道等觀念。 並且最重要的,是一門可以應用在電腦日常使用上的實際的程式語言。 對簡單學習過 c 語言的人, 配合 c 從 shell 認識電腦是一條十分有意義的道路。 本簡報從使用電腦的角度,帶領有 c 語言基礎的人認識 shell。


facebook 個人心得

這算我玩 linux 以來的小小願望吧。

一直覺得學校的資訊教育打不到點。雖然高中學過 C 語言,但那時的演算法競賽完全無法挑起我的興趣。直到大學接觸到 linux 和 shell 後才感受到什麼叫 學會使用電腦 。所以有人問怎麼學程式設計,我都直接回灌 linux,過程會用到指令,指令就是電腦運作最真實的模樣。最終,指令也會引導你走向程式語言,任何一種程式語言都可以;你才算是學會電腦。

現在單純學程式語言的很多,但會有個門檻是要能跨出 IDE,要知道可以怎麼應用。至少這是現在我回想高中時的心得。所以我比較推 shell。

希望這堂課能引起共鳴,至少讓其它人聽懂我在說什麼。


facebook 社課說明

在程式語言基礎上的 linux shell 指令教學

是否有經驗看著 linux 大神兩眼映照在漆黑的終端機中, 雙手運指如飛,指令在螢幕上傾洩而下的的模樣? 又或是否懷疑過,為什麼有一群人放著好好的 windows 不用, 而要特別在電腦上安裝另一種作業系統 linux? linux 的 shell,就是其中一個常見的答案。

linux 強大的 shell 指令介面是許多人對 linux 愛不釋手的原因; 但對新手而言,過度依靠 shell 也是讓 linux 難以入門的因素。 shell 的學習門檻高,使用上需要熟悉大量的指令,較圖形介面難以上手。

本次社課將從日常使用講起, 介紹 shell 如何是一門貼近使用者與務實的語言, 並穿插 shell 運作的基本邏輯, 使聽眾能將課堂所學的程式設計,實際運用到電腦的日常使用中, 自力成為 linux 大師。


靠北板發文

為什麼同學上完一學期 python 還是電腦白痴啊? 不過就簡單的檔案處理,寫個 one-liner 去跑很難嗎? 抱怨函數沒看過,也不會翻文件, 沒寫過如我不翻文件都看得懂大半了。 深深覺得台灣資訊教育真的缺了某個很重要的部份。

真想推薦他們 CCCA 這周五 10/4 19:00 在資訊中心電腦教室四的社課: 《在程式語言基礎上的 linux shell 指令教學》 聽說會從日常使用講起,介紹 shell 如何是一門貼近使用者與務實的語言, 讓聽眾能將課堂所學的程式設計,實際運用到電腦的日常使用中。 https://www.facebook.com/NCTUCCCA/posts/768329356934169

但主要還是要先有勇氣跨進 linux,至少裝個 cygwin 或 wsl。



大綱


可以實際應用的程式設計

高中電腦多半從 c 這種基本的,或其它 簡單的 程式語言教起, 但這些語言有個很大的問題, 他們都無法讓學生應用在日常的電腦操作上。


c 可以寫什麼


問題的關鍵


日常的操作


什麼是 shell


指令的特性


指令與圖形介面等價


shell 入門

指令是不含空白的一連串字元,一般包含底線與連字號。

pwd
ls

帶有參數的指令

參數以空白分隔

mv config.txt config_backup.txt
cp config_origin.txt config.txt
rm log.txt

移動到不同資料夾

cd my-dir
cd ..
cd /home
cd ../usr/bin

何謂開啟檔案?


開啟圖形程式

gedit
lowriter
firefox

用圖形程式開啟檔案

gedit file.txt
lowriter document.odt
firefox print.pdf

統一的指令

# linux
xdg-open file.odt
REM windows
start file.doc

REM or directly type filename
file.doc

快捷鍵


自動補全


上一個指令


在歷史中搜尋


用指令直接更改檔案

上面都是用指令 打開 一個圖形介面。 那能不能不透過圖形介面, 直接用指令完成對檔案的操作呢?


處理壓縮檔

# create zip homework.zip,
# which contain code.c report.odt Makefile
zip homework.zip code.c report.odt Makefile

看壓縮檔的內容

unzip homework.zip # directly unzip
unzip -l homework.zip # list content of zip

更改內容

# delete file in zip
zip -d homework.zip Makefile

# update file in zip
zip -u homework.zip code.c

選項


短選項與連寫


bsd 的長選項


gnu 的長選項


但選項的格式只是慣例


選項查詢

要怎麼知道命令怎麼下?有哪些選項?


求助選項

多數程式會有一個求助選項, 看到 --help-h 就會直接輸出簡短的說明。 當然也不是所有程式都有,還是那句話,看作者。


手冊


搜尋合適的指令


第一支腳本

# pack homework files into zip
zip homework.zip report.odt code.c

執行腳本

sh script.sh

加入提示

echo start packing file
zip homework.zip report.odt code.c
echo finish packing file

echo


輸出字串


printf

printf "file-%03d.txt\n" 12 # file-012.txt

輸出太長怎麼辦

剛剛說的 apropos

~:$ apropos zip
CPAN::Tarzip (3pm)   - internal handling of tar archives for CPAN.pm
Archive::Zip (3pm)   - Provide an interface to ZIP archive files.
Archive::Zip::FAQ (3pm) - Answers to a few frequently asked questions about A...
Archive::Zip::MemberRead (3pm) - A wrapper that lets you read Zip archive mem...
Archive::Zip::Tree (3pm) - (DEPRECATED) methods for adding/extracting trees u...
bunzip2 (1)          - a block-sorting file compressor, v1.0.6
bzcmp (1)            - compare bzip2 compressed files
bzdiff (1)           - compare bzip2 compressed files
bzegrep (1)          - search possibly bzip2 compressed files for a regular e...
bzfgrep (1)          - search possibly bzip2 compressed files for a regular e...
bzgrep (1)           - search possibly bzip2 compressed files for a regular e...
bzip2 (1)            - a block-sorting file compressor, v1.0.6
bzip2recover (1)     - recovers data from damaged bzip2 files
bzless (1)           - file perusal filter for crt viewing of bzip2 compresse...
bzmore (1)           - file perusal filter for crt viewing of bzip2 compresse...
Compress::Raw::Bzip2 (3perl) - Low-Level Interface to bzip2 compression library
CPAN::Tarzip (3perl) - internal handling of tar archives for CPAN.pm
docker-context-import (1) - Import a context from a tar or zip file
fcrackzip (1)        - a Free/Fast Zip Password Cracker
fcrackzipinfo (1)    - display zip information
funzip (1)           - filter for extracting from a ZIP archive in a pipe
gpg-zip (1)          - encrypt or sign files into an archive
gunzip (1)           - compress or expand files
gzip (1)             - compress or expand files
IO::Compress::Bzip2 (3perl) - Write bzip2 files/buffers
IO::Compress::Gzip (3perl) - Write RFC 1952 files/buffers
IO::Compress::Zip (3perl) - Write zip files/buffers
IO::Uncompress::AnyInflate (3perl) - Uncompress zlib-based (zip, gzip) file/b...
IO::Uncompress::AnyUncompress (3perl) - Uncompress gzip, zip, bzip2 or lzop f...
IO::Uncompress::Bunzip2 (3perl) - Read bzip2 files/buffers
IO::Uncompress::Gunzip (3perl) - Read RFC 1952 files/buffers
IO::Uncompress::Unzip (3perl) - Read zip files/buffers
MIME::Decoder::Gzip64 (3pm) - decode a "base64" gzip stream
p7zip (1)            - Wrapper on 7-Zip file archiver with high compression r...
pbzip2 (1)           - parallel bzip2 file compressor, v1.1.9
PerlIO::gzip (3pm)   - Perl extension to provide a PerlIO layer to gzip/gunzip
preunzip (1)         - prefix delta compressor for Aspell
prezip (1)           - prefix delta compressor for Aspell
prezip-bin (1)       - prefix zip delta word list compressor/decompressor
unzip (1)            - list, test and extract compressed files in a ZIP archive
unzipsfx (1)         - self-extracting stub for prepending to ZIP archives
zforce (1)           - force a '.gz' extension on all gzip files
zip (1)              - package and compress (archive) files
zipcloak (1)         - encrypt entries in a zipfile
zipdetails (1)       - display the internal structure of zip files
zipgrep (1)          - search files in a ZIP archive for lines matching a pat...
zipinfo (1)          - list detailed information about a ZIP archive
zipnote (1)          - write the comments in zipfile to stdout, edit comments...
zipsplit (1)         - split a zipfile into smaller zipfiles

用 less 接起來

apropos zip | less

其它用來接輸出的程式

apropos zip | head
apropos zip | tail

或是輸入到檔案

apropos zip > apropos-zip.txt
head < apropos-zip.txt

重導向或是直接指定檔名?

head < apropos-zip.txt
head apropos-zip.txt

有關 man

man zip # 開啟 less 讓使用者翻閱手冊
man zip > zip.txt # 把手冊存成文字檔

更改檔案內容

上面提過 echo printf,輸出也能重導向到檔案。

echo hey > hey
printf "hello world!" > hey

覆寫與附加

echo bye >> hey
printf "byby!" >> hey

組合 shell 命令

先前也提過,shell 最大的好處是可以寫成腳本執行, 可以省去用滑鼠點來點去的時間。


命令代換

$() 包起來的命令,執行後的結果會被置換到命令中。

用 wc 計算 ls 命令列出的第一個檔案大小。

wc -c $(ls | head -n 1)

變數賦值

sh 中,所有變數內容都是字串。

a=file.txt
b=$(ls | head -n 1)

變數取值

wc -c a # 計算檔名為 a 的大小
wc -c $a # 計算檔名為變數 a 的內容的大小

管道的力量

串連多個小程式,就能快速達到各種功能。

sed 's/[^[:alnum:]]/\n/g' | sort | uniq -c

網頁爬取

url='http://jamesiscurly.pixnet.net/blog/post/295281136-%e6%98%af%e6%97%a5%e6%a2%97%e5%9c%960621'
curl $url | grep -o '<img.*?>'
curl $url | tidy -asxml -q >meme.xml
xpath -e '//img' meme.xml
# or curl $url | tidy -asxml -q | xpath -e '//img'

程式


簡單的 c

// substr.c
int main(int argc, char **argv) {

  // argv
  int start;
  sscanf(argv[1], "%d", &start); // atoi()
  int end;
  sscanf(argv[2], "%d", &end);

  char string[80];
  fgets(string, sizeof(string), stdin); // scanf("%s", string)
  for (int i=start; i<end; i++) putchar(string[i]); // stdout
  putchar('\n');
  return 0;
}

編譯執行

~/code:$ gcc -o substrc substr.c
~/code:$ echo hello world | ./substrc 3 8
lo wo

如何讓程式變成命令


萬變不離宗


組合的力量

shell 本質即是組合大量行為單純的程式,從而達到複雜的功能。


shell 編程

shell 是一門完整的程式語言,當然帶有流程控制、迴圈等功能。


流程控制

if pwd | grep "^/home/$USER"
then echo inside home directory
else echo outside home directory
fi

控制原理


c 語言的返回值

int main(int argc, char **argv) {
  /* some code */
  if (true) return 0;
  else return error_code;
}

判斷迴圈

while pgrep wget
do
    echo wget is downloading
    sleep 1m
done

echo wget finish

while true
do
    if pgrep wget
    then
        echo wget is downloading
        sleep 1m
    else
        echo wget finish
        break
    fi
done

遍歷迴圈

for number in $(seq 10)
do
    file=$(printf "file-%02d.txt" $number)
    cat $file >> all.txt
done

判斷的命令

string=abc
test $string = abc # return 0
[ $string = abc ] # as same as test
if [ $n = 0 ]

c 實作

int main(int argc, char **argv) {
  return !strcmp(argv[1], argv[3]);
}
gcc -o test test.c
cp test [

執行檔


hashbang 變身執行檔


#!/usr/bin/python
print("hello world!")
#!/bin/sh
echo hello world!

以 test 為例

#!/usr/bin/node
# test.js
if (process.argv[2] == process.argv[4]) process.exit(0)
else process.exit(1)
chmod +x test.js
cp test.js [

選項的解析

另外,一開始有提過長短選項; 不管是哪一種風格選項,解析選項都不是一件容易的事。 有興趣可以自己試試看。 我之前寫的用 sh 解析選項的範例程式。


shell 與通用語言

shell 本身的功能很少,多數都是靠其它程式實現的。 例如上述示範的,字串比較這種基礎功能也可以靠外部實現。


shell 的特性


在一般程式語言中呼叫其它命令

system("zip -u homework.zip code.c");
execlp("zip", "-u", "homework.zip", "code.c");

一般程式語言特性


shell 是 linux 的統一介面


建議


以改檔名為例


gnss 觀測數據命名格式


批次工具


寫一個 python


直接用 python 輸出 shell 指令

print('mv', oldname, newname)

剪下貼上 shell 輸出

有時候,直接把 ls 的輸出複製到檔案, 再用 regexp 改成一堆 mv 還比較快。

ls | vi -
# in vi
# :%s/\(.*\).html/mv \1.html \1.htm/
# :%!sh

直接用 shell 解決

# rename Trim201904010000.rnx to NCTU0910.19o
for file in *
do
    old_format=$(date -d $(echo $file | substr 4 12) +"NCTU%03j0.%02yo")
    echo mv $file $old_format
    mv $file $old_format
done

shell 的優勢


其它程式語言


glob 萬用字元


萬用字元用例

mv * ../backup/
rename 's/.tar.gz$/.tgz/' *.tar.gz

# match filename longer then n word
for zip in ??????????????????????????*.zip
do
    basename=$(basename $(unzip -l $zip | grep -o '........\.csv') .pdf)
    mv $zip $basename.zip
done

GUI 程式


彈出視窗的命令


xmessage

if xmessage -buttons yes:0,no:2 'do you want to clean trash can?'
then gio trash --empty
fi

zenity

zenity --info --text 'hello zenity!'
if zenity --question --text 'do you want to give me your name?'
then
    name=$(zenity --entry --text 'what is your name?')
    zenity --info --text "hello $name"
fi

其它 x window 好用工具


結論


指令示範


把系統大檔案移到另一個硬碟

find -size +1G | while read path
do
    mv $path /mnt/backup/
    ln -s /mnt/backup/$(basename $path) $path
done

每 30 分鐘提醒一次眼睛休息

(
    while sleep 30m
    do zenity --info --text "30 分鐘了,眼睛休息一下吧!"
    done
) &

延伸閱讀