diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml new file mode 100644 index 00000000..9aa609c5 --- /dev/null +++ b/.github/workflows/macos.yml @@ -0,0 +1,21 @@ +name: macos-latest + +on: + push: + branches: [ main, sd/* ] + paths: ['src/**', 'test/**', '!README.md' ] + pull_request: + branches: [ main ] + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + + runs-on: macos-latest + + steps: + - uses: actions/checkout@v3 + - name: Bash tests + run: bash -xv ./test/test.bash diff --git a/.github/workflows/test.yml b/.github/workflows/ubuntu.yml similarity index 51% rename from .github/workflows/test.yml rename to .github/workflows/ubuntu.yml index 571a1dfb..2aae799b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/ubuntu.yml @@ -1,8 +1,9 @@ -name: Rust +name: ubuntu-latest on: push: - branches: [ main ] + branches: [ main, sd/* ] + paths: ['src/**', 'test/**', '!README.md' ] pull_request: branches: [ main ] @@ -15,10 +16,6 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Bash tests run: bash -xv ./test/test.bash - - name: Old tests - run: bash -xve ./test/old_test.bash - #- name: Internal tests - #run: cargo test --verbose diff --git a/README.md b/README.md index f6b20b49..42f7daa1 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,100 @@ -# Sushi Shell (sush) +# Rusty Bash (a.k.a. sushi 🍣 shell) + +[![ubuntu-latest](https://github.com/shellgei/rusty_bash/actions/workflows/ubuntu.yml/badge.svg)](https://github.com/shellgei/rusty_bash/actions/workflows/ubuntu.yml) +[![macos-latest](https://github.com/shellgei/rusty_bash/actions/workflows/macos.yml/badge.svg)](https://github.com/shellgei/rusty_bash/actions/workflows/macos.yml) +![](https://img.shields.io/github/license/shellgei/rusty_bash) + + +**IMPORTANT: the main branch is switched to the shell develped for articles on [SoftwareDesign](https://gihyo.jp/magazine/SD).** +(今までのメインブランチは、連載のものに比べて散らかりすぎなので、連載のものをmainに切り替えました。) + +* [old main branch](https://github.com/shellgei/rusty_bash/tree/old_main) + + +## What's this? + +A clone of Bash, which is developed as a hobby of our group and for monthly articles on SoftwareDesign magazine published by Gijutsu-Hyohron Co., Ltd. + +## Quick Start + +```bash +$ git clone https://github.com/shellgei/rusty_bash.git +$ cd rusty_bash +$ cargo build +... +🍣 echo hello +hello +🍣 exit +``` + +## For Contributors + +Since the main branch must synchronize the articles, further developments are reflected in the following branches. We prepared dev-* branches for adding your codes with pull requests. +* [dev-builtins](https://github.com/shellgei/rusty_bash/tree/dev-builtins): for builtin commands +* [dev-signal](https://github.com/shellgei/rusty_bash/tree/dev-signal): for development on signal +* [dev-compounds](https://github.com/shellgei/rusty_bash/tree/dev-compounds): for development on compoundss +* [dev-args](https://github.com/shellgei/rusty_bash/tree/dev-args): for development on arguments +* [dev-test](https://github.com/shellgei/rusty_bash/tree/dev-test): for development of a nice test system and test cases (we need more sophisticated test) +* [dev-others](https://github.com/shellgei/rusty_bash/tree/dev-others): for development of other features + +These dev-* branches will be merged to the main branch depending on the situation of the articles. You can also propose some dev-foobar branches. + +## List of Features + +* :heavy_check_mark: :available +* :construction: :partially available (or having known bugs) +* :no_good: : not implemented + + +### compound commands + +|features | status |features | status |features | status | +|-------------------|----|-------------------|----|-------------------|----| +| if | :no_good: | while | :no_good: | () | :heavy_check_mark: | +| {} | :heavy_check_mark: | case | :no_good: | until | :no_good: | select | :no_good: | +| for | :no_good: | + + +### control operator + +|features | status |features | status |features | status | +|-------------------|----|-------------------|----|-------------------|----| +| \|\| | :no_good: | && | :no_good: | ; | :heavy_check_mark: | +| ;; | :no_good: | \| | :heavy_check_mark: | & | :no_good: | +| \|& | :heavy_check_mark: | + +### builtin commands + +|features | status |features | status |features | status | +|-------------------|----|-------------------|----|-------------------|----| +| cd | :heavy_check_mark: | pwd | :no_good: | read | :no_good: | +| exit | :heavy_check_mark: | source | :no_good: | set | :no_good: | +| shopt | :no_good: | : | :no_good: | . | :no_good: | [ | :no_good: | +| alias | :no_good: | bg | :no_good: | bind | :no_good: | +| break | :no_good: | builtin | :no_good: | caller | :no_good: | +| command | :no_good: | compgen | :no_good: | complete | :no_good: | +| compopt | :no_good: | continue | :no_good: | declare | :no_good: | +| dirs | :no_good: | disown | :no_good: | echo | :no_good: | +| enable | :no_good: | eval | :no_good: | exec | :no_good: | +| fc | :no_good: | fg | :no_good: | getopts | :no_good: | +| hash | :no_good: | help | :no_good: | history | :no_good: | +| jobs | :no_good: | kill | :no_good: | let | :no_good: | +| local | :no_good: | logout | :no_good: | mapfile | :no_good: | +| popd | :no_good: | printf | :no_good: | pushd | :no_good: | +| read | :no_good: | readonly | :no_good: | return | :no_good: | +| shift | :no_good: | suspend | :no_good: | test | :no_good: | +| times | :no_good: | trap | :no_good: | true | :no_good: | +| type | :no_good: | typeset | :no_good: | ulimit | :no_good: | +| umask | :no_good: | unalias | :no_good: | unset | :no_good: | +| wait | :no_good: | export | :no_good: | false | :no_good: | + +## Thanks to + +Partially in Japanese. + +* blog articles + * [Rustでシェル作った | κeenのHappy Hacκing Blog](https://keens.github.io/blog/2016/09/04/rustdeshierutsukutta/) + * [Rustで始める自作シェル その1 | ぶていのログでぶログ](https://tech.buty4649.net/entry/2021/12/19/235124) + * [Rustのターミナル操作crateいろいろ | meganehouser](https://meganehouser.github.io/2019-12-11_rust-terminal-crates.html) + * [原理原則で理解するフォアグラウンドプロセスとバックグラウンドプロセスの違い | @tajima_taso](https://qiita.com/tajima_taso/items/c5553762af5e1a599fed) -This branch is for articles in Software Design. diff --git a/src/core.rs b/src/core.rs index 0a7176ff..2f37b063 100644 --- a/src/core.rs +++ b/src/core.rs @@ -62,7 +62,7 @@ impl ShellCore { if is_interactive() { core.flags += "i"; core.tty_fd = fcntl::fcntl(2, fcntl::F_DUPFD_CLOEXEC(255)) - .expect("Can't allocate fd for tty FD"); + .expect("sush(fatal): Can't allocate fd for tty FD"); } core.builtins.insert("cd".to_string(), builtins::cd); @@ -174,3 +174,4 @@ impl ShellCore { self.job_table.clear(); } } + diff --git a/src/elements/pipeline.rs b/src/elements/pipeline.rs index 9211304e..d3ec7753 100644 --- a/src/elements/pipeline.rs +++ b/src/elements/pipeline.rs @@ -22,7 +22,7 @@ impl Pipeline { for (i, p) in self.pipes.iter_mut().enumerate() { p.set(prev, pgid); pids.push(self.commands[i].exec(core, p)); - if i == 0 { // 最初のexecが終わったら、pgidにコマンドのPIDを記録 + if i == 0 && pgid.as_raw() == 0 { // 最初のexecが終わったら、pgidにコマンドのPIDを記録 pgid = pids[0].expect("SUSHI INTERNAL ERROR (unforked in pipeline)"); } prev = p.recv; diff --git a/test/test.bash b/test/test.bash index ed0df37c..5c49c4c3 100755 --- a/test/test.bash +++ b/test/test.bash @@ -73,8 +73,8 @@ res=$($com <<< '(echo hoge; false)') res=$($com <<< 'cd / ; (cd /etc); pwd') [ "$res" = / ] || err $LINENO -res=$($com <<< 'cd / ; { cd /etc ; } ; pwd') -[ "$res" = /etc ] || err $LINENO +res=$($com <<< 'cd ; { cd / ; } ; pwd') +[ "$res" = / ] || err $LINENO res=$($com <<< '( )') [ "$?" = 2 ] || err $LINENO @@ -107,27 +107,27 @@ res=$($com <<< 'eeeeeecho hoge') ### PIPELINE ### -res=$($com <<< 'seq 3 | tac | head -n 1') -[ "$res" = "3" ] || err $LINENO +res=$($com <<< 'seq 10 | rev | tail -n 1') +[ "$res" = "01" ] || err $LINENO -res=$($com <<< 'seq 3 | - tac | head -n 1') -[ "$res" = "3" ] || err $LINENO +res=$($com <<< 'seq 10 | + rev | tail -n 1') +[ "$res" = "01" ] || err $LINENO -res=$($com <<< 'seq 3 | +res=$($com <<< 'seq 10 | - tac | head -n 1') -[ "$res" = "3" ] || err $LINENO + rev | tail -n 1') +[ "$res" = "01" ] || err $LINENO -res=$($com <<< 'seq 3 | #コメントだよ +res=$($com <<< 'seq 10 | #コメントだよ #コメントだよ #こめんとだよ - tac | head -n 1') -[ "$res" = "3" ] || err $LINENO + rev | tail -n 1') +[ "$res" = "01" ] || err $LINENO -res=$($com <<< 'seq 3 | | head -n 1') +res=$($com <<< 'seq 10 | | head -n 1') [ "$?" = "2" ] || err $LINENO ### COMMENT ### @@ -176,7 +176,7 @@ res=$($com <<< 'echo hoge |\ & rev') [ "$res" = "egoh" ] || err $LINENO -res=$($com <<< ' (seq 3; seq 3) | grep 3 | wc -l') +res=$($com <<< ' (seq 3; seq 3) | grep 3 | wc -l | tr -dc 0-9') [ "$res" = "2" ] || err $LINENO res=$($com <<< 'ls | | rev') @@ -205,7 +205,7 @@ res=$($com <<< ' cd - > /tmp/rusty_bash2 cat /tmp/rusty_bash1 cat /tmp/rusty_bash2 - pwd') + pwd' | sed s@.private@@) [ "$res" = "/etc /tmp /tmp" ] || err $LINENO @@ -217,7 +217,7 @@ res=$($com <<< ' { cd - ; } > /tmp/rusty_bash2 cat /tmp/rusty_bash1 cat /tmp/rusty_bash2 - pwd') + pwd' | sed s@.private@@) [ "$res" = "/etc /tmp a @@ -230,19 +230,19 @@ res=$($com <<< ' ls /aaaa 2> /tmp/rusty_bash ls /bbbb 2>> /tmp/rusty_bash cat /tmp/rusty_bash | grep ls | wc -l - ') + ' | tr -dc 0-9) [ "$res" = "2" ] || err $LINENO # &> -res=$($com <<< 'ls /etc/passwd aaaa &> /tmp/rusty_bash; cat /tmp/rusty_bash | wc -l') +res=$($com <<< 'ls /etc/passwd aaaa &> /tmp/rusty_bash; cat /tmp/rusty_bash | wc -l | tr -dc 0-9') [ "$res" == "2" ] || err $LINENO # &> for non-fork redirects res=$($com <<< ' { ls /etc/passwd aaaa ; } &> /tmp/rusty_bash - cat /tmp/rusty_bash | wc -l') + cat /tmp/rusty_bash | wc -l | tr -dc 0-9') [ "$res" == "2" ] || err $LINENO res=$(LANG=C $com <<< ' @@ -252,7 +252,7 @@ res=$(LANG=C $com <<< ' { ls /etc/passwd ; } { ls aaaa ; } 2> /tmp/rusty_bash2 cat /tmp/rusty_bash2 | wc -l - ') + ' | tr -d [:blank:]) [ "$res" == "2 /etc/passwd 1" ] || err $LINENO @@ -264,7 +264,7 @@ res=$($com <<< ' { cd - ; } &> /tmp/rusty_bash2 cat /tmp/rusty_bash1 cat /tmp/rusty_bash2 - pwd') + pwd' | sed s@.private@@) [ "$res" = "/etc /tmp a