Skip to content

Instantly share code, notes, and snippets.

@nevikw39
Last active October 20, 2022 16:11
Show Gist options
  • Save nevikw39/06f049e5fc01878d27bd381beb1880a8 to your computer and use it in GitHub Desktop.
Save nevikw39/06f049e5fc01878d27bd381beb1880a8 to your computer and use it in GitHub Desktop.
CSST Linux Terminal Demo

CSST Linux Terminal Demo

這個 gist 主要是用以補充課堂上的 Demo 過於臨時倉促而可能沒有讓大家仔細觀察體驗,而這可能才是這堂課最重要的東西我竟然在有人發問之後才想到臨場發揮。

我個人覺得學習 Linux 不必想著一開始就記得所有指令,有什麼需求能夠在網路上搜尋到解決方案就可以了,常用的自然而然就會記起來。因此,以下的 demo 即使不一定能完全看懂,嘗試去猜或者理解,感受一下就行!!

看不太懂很正常也沒關係,可以照著步驟執行看看,體會操作 terminal 的感覺,這些指令都很安全沒有惡意也不會炸掉你的電腦,也可以動手改點參數、數字觀察會發生什麼事!!

Demo

回想一下情境,不過現在我們準備的題目是寫一個程式讀入整數 n, 要輸出 n 個整數和的兩倍。我們已經有一亂數產生測資的 Python script gen.py(其實厲害的人都用 "testlib.h" 生測資)和一個官解的程式(中國人愛稱標程?)sol.c. 假如我們想生 10 筆,甚至是 100 筆測資呢?總不可能慢慢打吧。

Basic operations

因此讓我們打開 terminal, 首先可以執行 pwd (Print Working Directory)echo $PWD ($ 代表變數,而 $PWD 是一個 shell 會自己定義的環境變數,他總是會是當下的 working directory) 確定當前的 working directory 與 shell 顯示的是否相同,接著可以利用 ls 常看目錄裡的檔案,cd 至放有 gen.py, sol.c 的目錄。或是 mkdir <名字> 建立一個資料夾,cd 進去後用任何編輯器 (such as vim ...) 建立 gen.py, sol.c. 最後可以 cat 這兩個檔案確定他們的內容。

編譯並測試一下 sol.c:

gcc sol.c -o sol # 平常 IDE 也是呼叫 `gcc` 幫我們編譯 source codes 為 binary file
./sol
# 輸入一些簡單測資,像是:2 87 69

可以試跑看看生測資的 script:

python3 gen.py

Redirections

如果想要把生測資的結果存起來,可以 redirect python3stdout 至檔案:

python3 gen.py > in.txt

這時理論上 terminal 不會有任何輸出,ls 之後可以發現倒是多了 in.txt 這個檔案,可以 cat 他一下。想要得到我們 C 程式的執行結果,可以把測資 redirect 至其 stdin:

./sol < in.txt

當然也可以把結果存入檔案:

./sol < in.txt > out.txt

之後執行以下指令就可以一次生十筆測資:

for i in {0..9}; do
    python3 gen.py > in$i.txt
done

這時候再 ls 一下會發現沒意外的話目錄多了十個檔案。如果想要生一千筆當然也不是問題。

Hadns-on!!

現在請自己動手修改一下範例,試看看利用迴圈把 in0.txt, in1.txt, ..., in9.txt 的標準答案輸出為 out0.txt, out1.txt, ..., out9.txt.

More example

現在你是一個 tester, 要幫我測試我出的題目是否正確。所以請寫一個讀入整數 n, 輸出接下來 n 個整數和的兩倍的程式(顯然會跟我的長得不太一樣)我姑且稱之 test.c 。寫好並編譯你的版本的程式為 ./test 後,如何比較你的輸出有沒有跟我的答案一致?你可以先想想、試試再看下去。

for i in {0..9}; do
    ./test < in$i.txt > test$i.txt
    diff test$i.txt out$i.txt # You can also use `cmp` command
done

如果不在乎測試程式的輸出,甚至可以這樣寫:

for i in {0..9}; do
    cmp <(./test < in$i.txt) out$i.txt # You can use `diff` command as well
done

Pipeline

如果我們生好測資之後,只想知道答案的範圍、長相之類的而甚至不想留測資呢?我們可能會這樣:

python3 gen.py > tmp.txt
./sol < tmp.txt
rm tmp.txt

很顯然 shell 會循序執行指令,也就是說,./sol 會等到 python3 跑完才被執行。我們可以利用工廠流水線 (| 這根也被稱作管道 pipe) 的原理,把 python3stdout 直接接到 ./solstdin 上,當 python3 print 一行 ./sol 就可以 scan 一行。如此一來,顯然流水線的效率較佳:

python3 gen.py | ./sol

你可以把兩種做法分別寫成 shell script 並用迴圈個跑十遍,如 sequential.sh, pipeline.sh, 然後執行 chmod 讓他們可以被執行,並用 time 測量所需的時間:

chmod +x *.sh # Add eXecution right to all .sh files
time ./sequential.sh
time ./pipeline.sh

我這裡自己測 pipeline 大約都 0.67~0.68s, sequential 則是 0.7~0.72s. 當然測資越大越多差異會越明顯。

但使用 pipeline 的話就不能保留 python3 gen.py 生成的測資嗎?這時,有個叫做 tee 的指令就派上用場了。他之所以叫 tee 的原因是因為可以想像他在流水線中就像一個 "Capital" T 或是說管道的分岔,可以把輸入存到一個檔案再傳給流水線的下一個人。因此,其實我們生測資及答案的 shell commands 可以如此改進:

for i in {0..9}; do
    python3 gen.py | tee in$i.txt | ./sol > out$i.txt
done

Pipeline 在 Computer Science 中很重要的觀念之一,現代 CPU 不小的平行部分包括指令管線化的設計。

Others

grep, sed, find, awk 等等也都是很好用的指令喔 o'_'o

真實案例之一

最近這幾天我都在忙邏實作業所以就拿來舉例,最終我們要把 Verilog codes 丟到系上工作站 simulate. 每次 lab 都有四、五個題目,每次都手打指令真的太苦了,假如有錯還要重來。因此我很早就寫成 shell scripts, 每次只要更新迴圈內容就可以一行跑完所有題目的 simulation.

不過系上工作站是使用 csh 而非 bash, 語法有一定差異所以只是跟大家分享可以參考參考。

#!/bin/tcsh

rm err.txt > /dev/null # We don't care the error message occured if err.txt is not existed

foreach i (Ping_Pong_Counter FIFO_8 Multi_Bank_Memory Round_Robin_FIFO_Arbiter Parameterized_Ping_Pong_Counter)
    ncverilog Lab3_Team30_$i.v Lab3_Team30_${i}_t.v +access+r || echo "- $i" >> err.txt; # write error to other file since `ncverilog` would print a lot
end

if (-e err.txt) then
    echo "Error!";
    cat err.txt;
else
    echo "AC!!";
endif

其他場合也很常用到 shell script 喔。

import random
n = 10**5
print(n)
print(' '.join(str(random.randrange(0, 10**9)) for _ in range(n)))
#!/bin/zsh
for i in {0..9}; do
python3 gen.py | ./sol
done
#!/bin/zsh
for i in {0..9}; do
python3 gen.py > tmp.txt
./sol < tmp.txt
rm tmp.txt
done
#include <stdio.h>
#include <stdint.h> // for int64_t
#include <inttypes.h> // for PRId64, do you know that 64-bit integers should be printed by `%lld` on Unix-like but `%l64d` on Wondows?
int main()
{
int n, a;
scanf("%d", &n); // This line is equivalent to `fscanf(stdin, "%d", &n);`
int64_t sum = 0; // Note that if n==10^5 && a_i==10^9, int will overflow
while (n--)
scanf("%d", &a), sum += a;
printf("%" PRId64 "\n", sum << 1); // This line is equivalent to `fprintf(stdout, "%" PRId64 "d\n", sum * 2);`
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment