---
# System prepended metadata

title: 開發工具指南 Part2

---

# 開發工具指南 Part2
時間：2023/11/09
講者：Ethan
[簡報連結](https://docs.google.com/presentation/d/1xYXvNkCLcJP9bsu8LSe3VPizCnoQC5Oe-luWiy2eUh8/edit#slide=id.p1)

# introduction
**什麼是shell**
- 用於存取作業系統服務的使用者介面
- 常見的 shell 包括 Bash、Zsh 和 Sh
- 用於執行命令、管理文件和運行腳本

**什麼是Bash scripting**
- script : 一個文字文件，包含一系列命令
- Bash script : 是以 Bash shell 語法編寫腳本實現日常任務、複雜操作等的自動化

**Bash vs Python**
![image.png](https://hackmd.io/_uploads/S1ZVZHq7T.png)

# Basics of the Shell
**GUI vs Command line**
- GUI: 直觀，適合簡單的任務
- Command Line: 強大、靈活、高效，可完成複雜任務

**Understanding the File System Tree**
Unix-like OS 有從 root 開始的樹狀目錄結構
- root: 包含所有檔案和子目錄
Unix-like systems 維護一個檔案系統樹
- Pathnames(路徑名稱):
絕對：從 root 開始（例如 /usr/bin）。
相對：從目前目錄開始，而不是根目錄。
- Special Notations:
.：表示目前工作目錄。
..：表示目前工作目錄的父目錄。

**Options and Arguments**
指令的結構
- command [-options] [arguments]
    - Options:
        短：破折號後的單一字元 e.g. -v
        長：兩個破折號後的單字 e.g. --version
        組合短選項：e.g. ls -lt
    - Arguments:
        命令操作的目標 e.g. filename, path, …
        
**基礎的shell指令**

- ls：列出目錄內容
- cd：更改目前目錄
- pwd：列印工作目錄
- echo：輸出文字到終端
- cat：輸出文件到終端
- clear：清除終端的輸出

**core commands**

- cp：複製檔案/目錄
- mv：移動/重新命名檔案/目錄
- mkdir：建立目錄
- rm：刪除檔案/目錄
- touch: 建立文件

**Wildcard Characters(萬用字元)**

- **\*** : 任意長度的字元
- **?** : 任意的單一字元

**使用'rm'時請小心**

- No Undo:
    Unix-like OS 缺乏「復原」功能。
    一旦使用 rm 刪除，很難找回來
- Wildcard Risks:
    可能會導致意外刪除
    Example:
        - Intended: rm *.html
        - Mistake: rm * .html

# Redirection

**Standard I/O Channels**
- Input (stdin): 通常來自鍵盤，也可以從檔案導入
- Output (stdout): 輸出，顯示在螢幕上或輸出到檔案
- Error (stderr): 錯誤訊息，顯示在螢幕上或輸出到檔案

**Basic Redirection**
- <: Input
```sort < unsorted.txt```
- \>: Redirects and truncates
```ls -l > output.txt```
- \>>: Redirects and appends
```ls -l >> output.txt```

**Error Redirection**

- 2>: 導出stderr
```ls -l /bin/usr 2> error.txt```

- 組合輸出和錯誤
\> file 2>&1 or &> file: stdout 和 stderr 輸出到相同檔案。
```ls -l /bin/usr &> ls-output.txt```

- 靜音輸出
/dev/null: 處理不需要的輸出
```ls -l /bin/usr 2> /dev/null```

**Exit Statuses**
- 0表示命令成功執行，沒有任何錯誤
- 其他（通常在1和之間255）都表示錯誤

**The Power of grep**
在指定文件中搜尋與 pattern(關鍵字) 相符的行，並列印它們。
Syntax(語法):
```grep [pattern] [filename]```

**Viewing File Extremities**
- head：顯示檔案的開頭
- tail：顯示文件結尾
```
Usage:
head -n 5 filename
```

**Pipelines**
Pipelines允許一個命令的輸出用作另一個命令的輸入
```
Syntax：
command1 | command2
```
```
example:
ls -l /usr/bin | grep "bash"
```


# Bash Scripting

**basic**
- Shebang
    - 由`#!`構成
    - 識別腳本的解釋器
    - #!/bin/bash
- 副檔名： .sh
- 注解：#

**Execute your script**
先讓你的script可執行
```
chmod +x script_name.sh
```
接著直接執行它
```
./script_name.sh
```

**Variables**
- 語法：
```
#variable=value
a=5
```
(錯誤範例)
```
a = 5
a= 5
a =5
```
- 要呼叫變數時使用'$'
```
a="a string"
b="a string and $a"
c="$(ls -1 foo.txt)"
FOO=1
```
(錯誤範例)
```
$FOO + 1 #Error
```

- 輸入
```
# read -p ["一些你想輸入的文字"] [目標]
read -p "send: " FOO
```

**Argument**
- $0 - 腳本名稱
- $1-$9 - 腳本的參數。 $1 是第一個參數，依此類推。
- $@ - 所有參數
- $# - 參數數量
- $? - 上一個指令的返回碼

**Conditionals**
```
false || echo "Oops, fail"
# Oops, fail

true || echo "Will not be printed"
#

true && echo "Things went well"
# Things went well

false && echo "Will not be printed"
#

true ; echo "This will always run"
# This will always run

false ; echo "This will always run"
# This will always run

```
一些常用符號
- -eq：==
- -ne：!=
- -gt：>
- -ge：>=
- -lt：<
- -le：<=

**File Expressions**
- -d file：如果檔案存在且是目錄，則為 true。
- -e file：如果檔案存在則為true。
- file1 -nt file2：如果 file1 比 file2 新，則為 True。
- file1 -ot file2：如果 file1 早於 file2，則為 True。

**Conditionals**
```
if [ condition1 ]; then
  COMMANDS1
elif [ condition2 ]; then
  COMMANDS2
else
  COMMANDS3
fi
```
**Case**
```
case <expression> in
  pattern1)
    commands1;;
  pattern2)
    commands2;;
  *)
    default_commands;;
esac
```
```
case $1 in
  start)
    echo "Starting the service...";;
  stop)
    echo "Stopping the service...";;
  restart)
    echo "Restarting the service...";;
  *)
    echo "Usage: $0 {start|stop|restart}";;
esac
```

**for loops**
```
NAMES="a b c d"
for NAME in $NAMES
do
    echo "Hello $NAME"
done
```
(c language form)
```
for ((expression1; expression2; expression3 )); do
    commands
done
```
```
#!/bin/bash
# simple_counter: demo of C style for command
for ((i=0; i<5; i=i+1)); do
    echo $i
done

```
**Sequence**
- seq LAST
- seq FIRST LAST
- seq FIRST INCREMENT LAST

```
for i in $(seq 1 5); do
    echo "Number $i"
done
```
**while loops**
```
while TEST-COMMANDS; do
  CONSEQUENT-COMMANDS
done
```
```
#!/bin/bash
# while-count: display a series of numbers
count=1
while [[ "$count" -le 5 ]]; do
      echo "$count"
      count=$((count + 1))
done
echo "Finished."
```
**function**
```
function function_name () {
  # Code to execute
  commands
  return [n] # Optional
}
```
```
function greet {
  echo "Hello!"
}
```
```
#!/bin/bash
# Define the function
greet () {
  echo "Hello, $1!"
}
# Call the function
greet "Alice"
greet "Bob"
```
# NOTE
- 不要執行自己不認識的命令
- 不會寫就找 ChatGPT 寫
- 組合不同的程式一起使用
e.g. python + bash

# 練習答案
## ex1
`mkdir ex1`
`mkdir ex1/dir1 ex1/dir2`
`cat /etc/issue`
`cp /etc/issue ex1/dir1`
`cp /etc/issue ex1/dir2`
`mv ex1/dir2/issue ex1/dir2/sys`

## ex2
`grep -rn "handle__subscribe" .`


## ex3
```bash=
#!/bin/bash

if [ $# -eq 0 ]; then
    echo "Usage: $0 extension"
    exit 1
fi

# Create the directory if it doesn't exist
[ -d "$1" ] || mkdir "$1"

# Move the files
mv *."$1" "$1"
```

## ex4
```bash=
#!/bin/bash
echo "Menu"
echo "1) date\n 2) ls\n 3) who\n 4) pwd\n 5) exit"
while :
do
  read -p "Enter your choice: " choice
  case $choice in
    1)
      date;;
    2)
      ls;;
    3)
      who;;
    4)
      pwd;;
    5)
      exit;;
    *)
      echo "Invalid choice"
      echo "1) date\n 2) ls\n 3) who\n 4) pwd\n 5) exit";;
  esac
done


```

## ex5
```bash!=
#!/bin/bash
if [ $# -eq 0 ]; then
    echo "fib needs arg"
    exit 1
fi

fib() {
    N="$1"
    if [ "$N" -eq 0 ]; then
        echo 0
    elif [ "$N" -eq 1 ]; then
        echo 1
    else
        echo $(( $( fib $((N-1)) ) + $( fib $((N-2)) ) ))
    fi
}

for i in $( seq 0 "$1" ); do
    fib "$i"
done
```
## ex6
```bash=
#!/bin/bash

if [ "$#" -ne 2 ]; then
	echo "Usage: $0 <file_without_extension> <number_of_testcases>"
	exit 1
fi
hw_name="$1"
# compile the program
g++ "$hw_name".cpp -o "$hw_name"

# get the number of testcases
for i in  $( seq 1 $2 ); do
	test_in="./testcases/input/$1-$i.in"
	test_out="./testcases/output/$1-$i.out"
	tmp_output="./testcases/tmp_output/"$1"-"$i".out"
	if [ -e "$test_in" ]; then
		# run the program, input from test_in, output to tmp_output
		./"$1" < $test_in > $tmp_output
		test_case=$1-$i
		# use diff to compare the output and redirect the unwanted output to /dev/null
		if diff -q "$tmp_output" "$test_out" > /dev/null; then
			echo "Test $test_case passed"
		else
			echo "Test $test_case failed"
		fi
	else
		echo "Input file $test_in does not exist"
	fi
done
```




















