私は本当にこのチュートリアルを書きたくありませんが、既存のバッシュチュートリアルがあまりにもひどいので、仕方がありません。本、ウェブサイト、YouTubeを見ましたが、どれもひどいです。基本から始めていません。無駄なものがたくさん含まれています。そして、コアコンセプトを説明していません。だから、私はこのリアクショナリープログラミングを学ぶバッシュレッスンのためにこれを書くしかありません。
バッシュはシェルの一つで、私が好むものです。私はMacとWindowsに焦点を当てます。Linuxは持っていませんし、嫌いなので、話しません。バッシュのほとんどはMacとWindowsで同じですが、異なる部分については両方を説明します。
バッシュへのアクセス方法は、オペレーティングシステムによって異なります。Macの場合、「アプリケーション > ユーティリティ > Terminal.app」にあるMacターミナルを通じてバッシュにアクセスします。デフォルトのシェルをバッシュに設定してください。Windowsの場合はMSYS2をインストールします。デフォルトのターミナルはあまり良くないので、Windowsターミナルを使用することをお勧めします。
Macでバッシュを起動すると、次のように表示されます:
Last login: Thu Jan 4 23:25:35 on ttys004
The default interactive shell is now zsh.
To update your account to use zsh, please run `chsh -s /bin/zsh`.
For more details, please visit https://support.apple.com/kb/HT208050.
~ $
Windows - MSYS2では、次のように表示されます:
~ $
$の行はコマンドプロンプトです。カーソルはその末尾にあり、入力するとテキストがそこに表示されます。$の前に異なるテキストが表示されることがありますが、それは問題ありません。ただし、行は$で終わる必要があります。そうでない場合は、何かが間違っています。
「qqq」と入力してください。「何かを入力」と言った場合は、最後にリターン/エンターを入力してください。リターン/エンターを入力したときにのみ、バッシュは入力した内容を処理します。次のように表示されるはずです:
~ $ qqq
-bash: qqq: command not found
~ $
バッシュは「qqq」が何を意味するのか分からず、そのように表示します。次に、以下を試してください... $の後にあるものを入力し、バッシュが示されているように応答するはずです。
~ $ echo hi
hi
~ $ echo how are you
how are you
~ $ echo bye
bye
echoコマンドは、後に続くものをエコーします。次に、キーボードの上矢印を押してください。これにより、カーソルの位置に前のコマンドが表示されます。もう一度上矢印を押すと、その前のコマンドが表示されます。下矢印、左矢印、右矢印を試してください。これを使用して、コマンド履歴をナビゲートできます。削除キーも行の編集に使用できます。そしてもちろん、入力できます。リターン/エンターを押すと、バッシュは編集されたコマンドを取得して処理します。
echo how are youを入力すると、echoはコマンドです。このコマンドには3つの引数があります: how、are、you。コマンドと引数はスペースで区切られます。スペースの数は関係ありませんので、次のように:
~ $ echo how are you
how are you
echoは引数を1つのスペースで区切って返します。
~ $ echo one; echo two
one
two
1行に複数のコマンドを;で区切って配置できます。
次を入力してください:
~ $ man echo
次のようなものが表示されるはずです:
ECHO(1) BSD General Commands Manual ECHO(1)
NAME
echo -- write arguments to the standard output
SYNOPSIS
echo [-n] [string ...]
DESCRIPTION
The echo utility writes any specified operands, separated by single blank
(` ') characters and followed by a newline (`\n') character, to the stan-
dard output.
The following option is available:
-n Do not print the trailing newline character. This may also be
achieved by appending `\c' to the end of the string, as is done by
iBCS2 compatible systems. Note that this option as well as the
effect of `\c' are implementation-defined in IEEE Std 1003.1-2001
(``POSIX.1'') as amended by Cor. 1-2002. Applications aiming for
maximum portability are strongly encouraged to use printf(1) to
suppress the newline character.
:
しかし、Windowsではmanがインストールされていないかもしれません。その場合は、次を行ってください:
~ $ pacman -S man-db
ここで説明されているようにmanをインストールし、再度man echoを試してください。
manコマンドはコマンドのドキュメントを表示します。残念ながら、キーを記憶することに基づいた愚かなユーザーインターフェースを持っているので、必要なキーをいくつかだけ教えます。下矢印と上矢印は1行ずつ下と上に移動します。スペースキーは1ページ下に移動します。そして最も重要なのは、「q」を入力すると終了し、バッシュに戻ります。これを覚えておく必要があります。
次にman manを入力してみてください。表示されるすべての情報は必要ありませんが、複雑なmanページがどのように見えるかを確認できます。他のコマンドのドキュメントを取得するためにmanを使用できます。
Mac FinderまたはWindowsファイルエクスプローラーに精通しているはずで、これからディレクトリ(「フォルダ」とも呼ばれる)がツリー状に整理されていることを知っているはずです。
Macの場合:
~ $ pwd
/Users/fschmidt
~ $ open .
~ $
Windowsの場合:
~ $ pwd
/home/fschmidt
~ $ explorer .
~ $
バッシュを使用する際、常にあるディレクトリにいます。これをカレントディレクトリまたは作業ディレクトリと呼びます。pwdはこのディレクトリへのフルパスを表示します。詳細はman pwdを参照してください。open .はカレントディレクトリのMac Finderを開き、explorer .はカレントディレクトリのWindowsファイルエクスプローラーを開くはずです。
Macで続けます:
~ $ mkdir learn
~ $ cd learn
~/learn $ pwd
/Users/fschmidt/learn
mkdirはカレントディレクトリにディレクトリを作成します。作成されたディレクトリがMac FinderまたはWindowsファイルエクスプローラーに表示されるはずです。cdは「ディレクトリを変更する」の略です。これによりカレントディレクトリが変更されます。cdはバッシュに組み込まれたコマンドであり、manは組み込みコマンドには役立たないため、man cdの代わりにhelp cdを試してください。続けます...
~/learn $ pwd
/Users/fschmidt/learn
~/learn $ ls
~/learn $ touch file1
~/learn $ ls
file1
~/learn $ touch file2
~/learn $ touch file3
~/learn $ ls
file1 file2 file3
~/learn $ mkdir dir1
~/learn $ ls
dir1 file1 file2 file3
~/learn $ ls -F
dir1/ file1 file2 file3
~/learn $ ls -a
. .. dir1 file1 file2 file3
~/learn $ ls -a -F
./ ../ dir1/ file1 file2 file3
~/learn $ ls -aF
./ ../ dir1/ file1 file2 file3
lsはファイルをリストし、touchは空のファイルを作成します。-で始まる引数はオプションです。使用したオプションが何をするかを確認するにはman lsを参照してください。-Fはディレクトリに「/」を追加し、-aは通常隠されている「.」で始まるファイルを表示します。オプションは組み合わせることができます。
~/learn $ ls file1
file1
~/learn $ ls qqq
ls: qqq: No such file or directory
~/learn $ ls file1 qqq file2
ls: qqq: No such file or directory
file1 file2
~/learn $ ls dir1
~/learn $ touch dir1/d1file
~/learn $ ls dir1
d1file
~/learn $ ls -d dir1
dir1
~/learn $ ls file1 file2 dir1
file1 file2
dir1:
d1file
~/learn $ ls -d file1 file2 dir1
dir1 file1 file2
~/learn $ ls -dF file1 file2 dir1
dir1/ file1 file2
ファイル引数なしでlsはカレントディレクトリのファイルをリストします。ファイル引数がある場合、それらのファイルが存在する場合にリストします。ファイルがディレクトリの場合、-dオプションが使用されない限り、そのディレクトリ内のものをリストします。
~/learn $ ls
dir1 file1 file2 file3
~/learn $ ls .
dir1 file1 file2 file3
~/learn $ ls -d .
.
~/learn $ ls -dF .
./
~/learn $ ls ./file1
./file1
~/learn $ ls dir1
d1file
~/learn $ ls ./dir1
d1file
~/learn $ pwd
/Users/fschmidt/learn
~/learn $ cd .
~/learn $ pwd
/Users/fschmidt/learn
.はカレントディレクトリです。
~/learn $ pwd
/Users/fschmidt/learn
~/learn $ cd dir1
~/learn/dir1 $ pwd
/Users/fschmidt/learn/dir1
~/learn/dir1 $ ls .
d1file
~/learn/dir1 $ ls ..
dir1 file1 file2 file3
~/learn/dir1 $ cd ..
~/learn $ pwd
/Users/fschmidt/learn
~/learn $ cd dir1
~/learn/dir1 $ pwd
/Users/fschmidt/learn/dir1
~/learn/dir1 $ cd ../..
~ $ pwd
/Users/fschmidt
~ $ cd learn
~/learn $ pwd
/Users/fschmidt/learn
..は親ディレクトリです。
~/learn $ echo *
dir1 file1 file2 file3
~/learn $ echo d*
dir1
~/learn $ echo f*
file1 file2 file3
~/learn $ echo *1
dir1 file1
~/learn $ echo dir1/*
dir1/d1file
~/learn $ echo */*
dir1/d1file
~/learn $ echo qqq*
qqq*
*はファイルのワイルドカードマッチングを行います。重要なのは、バッシュがワイルドカードマッチングを行い、その結果の引数をコマンドに渡すことです。echoは一致がない限り「*」を見ません。
~/learn $ ls *
file1 file2 file3
dir1:
d1file
~/learn $ ls -dF *
dir1/ file1 file2 file3
~/learn $ ls -dF d*
dir1/
~/learn $ ls -dF f*
file1 file2 file3
~/learn $ ls -dF *1
dir1/ file1
~/learn $ ls dir1/*
dir1/d1file
~/learn $ ls */*
dir1/d1file
~/learn $ ls -dF qqq*
ls: qqq*: No such file or directory
自明であるべきです。
~/learn $ pwd
/Users/fschmidt/learn
~/learn $ cd ~
~ $ pwd
/Users/fschmidt
~ $ cd learn/dir1
~/learn/dir1 $ pwd
/Users/fschmidt/learn/dir1
~/learn/dir1 $ cd
~ $ pwd
/Users/fschmidt
~ $ cd ~/learn
~/learn $ pwd
/Users/fschmidt/learn
~/learn $ echo ~
/Users/fschmidt
~/learn $ echo .
.
~/learn $ echo ..
..
~はホームディレクトリを意味します。引数なしのcdはcd ~と同じです。~はバッシュによってホームディレクトリに展開されます。
~/learn $ ls -ltF
total 0
drwxr-xr-x 3 fschmidt staff 96 Jan 5 02:33 dir1/
-rw-r--r-- 1 fschmidt staff 0 Jan 5 02:21 file3
-rw-r--r-- 1 fschmidt staff 0 Jan 5 02:21 file2
-rw-r--r-- 1 fschmidt staff 0 Jan 5 02:21 file1
-lはこの醜い技術的な形式を提供します。ファイルが最後に変更された日付が表示されます。日付の前にはファイルサイズがあります。-tは日付の降順でソートします。
最後にオートコンプリートについて説明します。echo dと入力し、エンター/リターンを押さずにタブキーを押します。これによりecho dir1/にオートコンプリートされます。もう一度タブを押すとecho dir1/d1fileにオートコンプリートされます。ファイルやディレクトリを入力中にタブを押すと、バッシュが一致するファイル名を使用してオートコンプリートを試みます。echo fを入力してタブを押すとecho fileになります。次にどれを選ぶべきか分かりません。もう一度タブを押すとビープ音が鳴ります。そしてもう一度タブを押すと次のようにオプションが表示されます:
~/learn $ echo file
file1 file2 file3
~/learn $ echo file
一般的に、ファイル名を入力中にいつでもタブを押してみてください。オートコンプリートは多くの入力を節約します。
~/learn $ ls -F
dir1/ file1 file2 file3
~/learn $ cp file1 copied
~/learn $ ls -F
copied dir1/ file1 file2 file3
~/learn $ mv copied moved
~/learn $ ls -F
dir1/ file1 file2 file3 moved
~/learn $ rm moved
~/learn $ ls -F
dir1/ file1 file2 file3
cpはファイルやディレクトリをコピーします。mvはファイルやディレクトリを移動します。rmはファイルやディレクトリを削除します。詳細はこれらのコマンドのmanページを参照してください。
~/learn $ ls -F
dir1/ file1 file2 file3
~/learn $ mkdir dir2
~/learn $ touch dir2/d2file
~/learn $ ls -F
dir1/ dir2/ file1 file2 file3
~/learn $ ls dir2
d2file
~/learn $ rm dir2
rm: dir2: is a directory
~/learn $ rm -d dir2
rm: dir2: Directory not empty
~/learn $ rm dir2/d2file
~/learn $ rm -d dir2
~/learn $ ls -F
dir1/ file1 file2 file3
~/learn $ ls -F
dir1/ file1 file2 file3
~/learn $ mkdir dir2
~/learn $ touch dir2/d2file
~/learn $ ls -F
dir1/ dir2/ file1 file2 file3
~/learn $ rm -r dir2
~/learn $ ls -F
dir1/ file1 file2 file3
~/learn $ ls -F
dir1/ file1 file2 file3
~/learn $ cp dir1 dir2
cp: dir1 is a directory (not copied).
~/learn $ cp -r dir1 dir2
~/learn $ ls -F
dir1/ dir2/ file1 file2 file3
~/learn $ ls dir2
d1file
~/learn $ cp f* dir2
~/learn $ ls dir2
d1file file1 file2 file3
~/learn $ rm -r dir2
~/learn $ ls -F
dir1/ file1 file2 file3
~/learn $ ls -F
dir1/ file1 file2 file3
~/learn $ mkdir dir2
~/learn $ cp -r dir1 dir2
~/learn $ ls -F dir2
dir1/
~/learn $ ls -F dir2/dir1
d1file
~/learn $ rm -r dir2
~/learn $ ls -F
dir1/ file1 file2 file3
これをすべて説明することもできますが、しません。manを使用してコマンドとそのオプションを理解し、それらを試してみてください。完全に理解するまで続けないでください。
~/learn $ echo a b
a b
~/learn $ echo "a b"
a b
~/learn $ echo 'a b'
a b
~/learn $ echo "a b" c
a b c
バッシュは引用符内のテキストを1つの引数として扱います。したがって、echo a bでは、echoには2つの引数があります: "a"と"b"。echo "a b"では、echoには1つの引数があります: "a b"。echo 'a b'では、echoには1つの引数があります: "a b"。echo "a b" cでは、echoには2つの引数があります: "a b"と"c"。
~/learn $ echo a\ \ \ b
a b
引用符の外では、\ は区切り文字として扱われず、引数の一部としてスペース文字として扱われます。
~/learn $ echo file*
file1 file2 file3
~/learn $ echo "file*"
file*
~/learn $ echo 'file*'
file*
引用符はワイルドカード展開を防ぎます。
~/learn $ echo $X
~/learn $ X="some text"
~/learn $ echo $X
some text
~/learn $ echo "X is: $X"
X is: some text
~/learn $ echo 'X is: $X'
X is: $X
~/learn $ X="$X and more"
~/learn $ echo $X
some text and more
ここでXは変数です。$Xでその値を取得します。これはダブルクォート内でも機能しますが、シングルクォート内では機能しません。
バッシュで使用される特別な変数を環境変数と呼びます。
~/learn $ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/fschmidt/Dropbox/bin:/Users/fschmidt/hg/luan/scripts:/usr/local/opt/postgresql@9.5/bin
~/learn $ which ls
/bin/ls
~/learn $ cd /bin
/bin $ pwd
/bin
/bin $ ls
[\t\tdd\t\tlaunchctl\tpwd\t\ttest
bash\t\tdf\t\tlink\trm\t\tunlink
cat\t\techo\t\tln\trmdir\t\twait4path
chmod\t\ted\t\tls\tsh\tzsh
cp\t\texpr\t\tmkdir\tsleep
csh\t\thostname\tmv\tstty
dash\t\tkill\tpax\tsync
date\t\tksh\tps\ttcsh
/bin $ ls -F
[*\t\tdd*\t\tlaunchctl*\tpwd*\t\ttest*
bash*\t\tdf*\t\tlink*\trm*\t\tunlink*
cat*\t\techo*\t\tln*\trmdir*\t\twait4path*
chmod*\t\ted*\t\tls*\tsh*\tzsh*
cp*\t\texpr*\t\tmkdir*\tsleep*
csh*\t\thostname*\tmv*\tstty*
dash*\t\tkill*\tpax*\tsync*
date*\t\tksh*\tps*\ttcsh*
/bin $ cd ~/learn
~/learn $
PATHはバッシュがコマンドを検索するディレクトリのリストを:で区切って含む環境変数です。whichコマンドはコマンドへのフルパスを表示します。ls -Fは実行可能ファイルに*を追加します。
~/learn $ subl file1
-bash: subl: command not found
~/learn $ "/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl" file1
~/learn $ PATH="$PATH:/Applications/Sublime Text.app/Contents/SharedSupport/bin"
~/learn $ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/fschmidt/Dropbox/bin:/Users/fschmidt/hg/luan/scripts:/usr/local/opt/postgresql@9.5/bin:/Applications/Sublime Text.app/Contents/SharedSupport/bin
~/learn $ subl file1
~/learn $
ここでは、Sublime Textでファイルfile1を編集します。最初はフルパスを使用し、その後ディレクトリをPATHに追加してバッシュがsublを見つけられるようにします。
WindowsでMicrosoft Wordを持っています。Windowsコマンドプロンプト(バッシュではなく)から:
C:\Users\fschmidt>winword
C:\Users\fschmidt>where winword
C:\Program Files\Microsoft Office\root\Office16\WINWORD.EXE
winwordはMicrosoft Wordを実行します。コマンドプロンプトのwhereコマンドはバッシュのwhichコマンドのようなものです。では、MSYS2で:
~ $ winword
bash: winword: command not found
~ $ echo $PATH
/usr/local/bin:/usr/bin:/bin:/opt/bin:/c/Windows/System32:/c/Windows:/c/Windows/System32/Wbem:/c/Windows/System32/WindowsPowerShell/v1.0/:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/c/Program Files/TortoiseHg:/c/Program Files/Java/jdk1.8.0_202/bin
~ $ PATH="$PATH:/c/Program Files/Microsoft Office/root/Office16"
~ $ echo $PATH
/usr/local/bin:/usr/bin:/bin:/opt/bin:/c/Windows/System32:/c/Windows:/c/Windows/System32/Wbem:/c/Windows/System32/WindowsPowerShell/v1.0/:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/c/Program Files/TortoiseHg:/c/Program Files/Java/jdk1.8.0_202/bin:/c/Program Files/Microsoft Office/root/Office16
~ $ winword
~ $
Macに戻ると、Finderの「アプリケーション」にあるアプリケーションをコマンドとしてではなくアプリケーションとして実行する別の方法があります。
~/learn $ open -a 'Sublime Text' file1
もう一つの便利な環境変数はPS1で、コマンドプロンプトを制御します。私はすでにこれを設定していますが、もししていなかったら:
Franklins-MacBook-Pro:learn fschmidt$ echo $PS1
\h:\W \u\$
Franklins-MacBook-Pro:learn fschmidt$ PS1="\w $ "
~/learn $ echo $PS1
\w $
~/learn $
「bash PS1」をGoogleで検索して詳細を確認してください。
~/learn $ cd
~ $ ls .bash_profile
.bash_profile
もし.bash_profileが見つからない場合は、touch .bash_profileを実行して作成してください。このファイルには、バッシュが起動したときに実行されるバッシュコマンドが含まれています。すでにこのファイルがある場合は、#で始まるコメントが含まれている可能性があります。コメントは次のように無視されます:
~ $ # コメント行、何もしません
~ $ echo whatever # 行末コメント
whatever
~ $
Macで.bash_profileを編集するには、次を実行できます:
~ $ open -a 'Sublime Text' .bash_profile
Windowsで.bash_profileを編集するには、次を実行できます:
~ $ notepad .bash_profile
次の行を.bash_profileに追加してみてください:
echo hello there
新しいバッシュターミナルを開くと、「hello there」が表示されるはずです。.bash_profileは、新しいバッシュターミナルを開くことでバッシュが開始されたときに実行されます。
私はPS1とPATHを.bash_profileに設定して、望むコマンドプロンプトとアクセスしたいコマンドを持つようにしています。Sublime TextコマンドsublをPATHで利用可能にすることをお勧めします。
~/learn $ find .
.
./file3
./file2
./file1
./dir1
./dir1/d1file
~/learn $ find . -name 'file*'
./file3
./file2
./file1
~/learn $ find . -name '*file'
./dir1/d1file
~/learn $ find . -name 'd*'
./dir1
./dir1/d1file
~/learn $ find . -name '*1' -or -name '*2'
./file2
./file1
./dir1
findはディレクトリツリー内のファイルを再帰的に検索します。この場合、*のワイルドカードマッチングはバッシュによって行われているのではなく、findによって行われています。findにはファイルを検索し、それに対してアクションを実行するための多くのオプションがあります。man findを参照してください。
~/learn $ echo 'this is a test' >test.txt
~/learn $ ls -F
dir1/ file1 file2 file3 test.txt
~/learn $ cat test.txt
this is a test
~/learn $ echo 'this is another test' >test.txt
~/learn $ cat test.txt
this is another test
~/learn $ echo 'another line' >>test.txt
~/learn $ cat test.txt
this is another test
another line
~/learn $ cat <test.txt
this is another test
another line
~/learn $ cat <<End >test.txt
> I am typing this
> and this
> End
~/learn $ cat test.txt
I am typing this
and this
~/learn $ (echo one; echo two) >test.txt
~/learn $ cat test.txt
one
two
すべてのプログラムには標準入力、標準出力、標準エラーがあります。プログラムは通常の出力を標準出力に書き込み、エラーメッセージを標準エラーに書き込みます。デフォルトでは、標準入力はターミナルから来て、標準出力と標準エラーはターミナルに行きますが、これを変更できます。>fileは標準出力をfileに送ります。>>fileは標準出力をfileに追加します。<fileは標準入力をfileから読み込みます。<<whateverはwhateverだけの行が来るまで続くテキストから標準入力を読み込みます。コマンドは(と)の間で組み合わせることができます。catの動作を理解するためにman catを必ず行ってください。
~/learn $ ls >ls.txt
~/learn $ cat ls.txt
dir1
file1
file2
file3
ls.txt
test.txt
~/learn $ ls -d f* q* >ls.txt
ls: q*: No such file or directory
~/learn $ cat ls.txt
file1
file2
file3
~/learn $ ls -d f* q* 2>ls.txt
file1 file2 file3
~/learn $ cat ls.txt
ls: q*: No such file or directory
~/learn $ ls -d f* q* | tee ls.txt
ls: q*: No such file or directory
file1
file2
file3
~/learn $ cat ls.txt
file1
file2
file3
~/learn $ ls -d f* q* 2>&1 | tee ls.txt
ls: q*: No such file or directory
file1
file2
file3
~/learn $ cat ls.txt
ls: q*: No such file or directory
file1
file2
file3
2>fileは標準エラーをfileに送ります。|は前のコマンドの標準出力を次のコマンドの標準入力に送ります。2>&1は標準エラーを標準出力に送ります。tee fileは標準入力を読み取り、それを標準出力とfileの両方に書き込みます。
~/learn $ find . -type f | wc -l
6
learnには6つのファイルがあります。これがどのように機能するかを理解するためにmanを使用してください。
~/learn $ sleep 3
~/learn $ sleep 30
^C
~/learn $
sleep 3は3秒間スリープします。つまり、3秒間何もしません。このコマンドが終了するのを3秒待ちました。次にsleep 30を実行しましたが、30秒間スリープするはずでしたが、我慢できずにcontrol+cを押してプログラムを中断し、抜け出しました。コマンドが終了するのを待っているときに詰まった場合は、control+cを試してみてください。
~/learn $ wc
I am typing this
and this
now I will end my input with control+d
3 14 65
~/learn $ wc
this time I will use control+c to break out
^C
~/learn $
Control+dは入力の終了を意味します。
~/learn $ echo I am in $(pwd)
I am in /Users/fschmidt/learn
~/learn $ echo this directory contains: $(ls)
this directory contains: dir1 file1 file2 file3 ls.txt test.txt
~/learn $ echo this directory contains $(ls | wc -l) files
this directory contains 6 files
cmd $(commands)はcommandsの出力をcmdの引数テキストとして使用します。
~/learn $ cat $(find . -type f) | wc -c
86
learnのファイルには合計86バイトが含まれています。これがどのように機能するかを理解するためにmanを使用してください。
~/learn $ (sleep 5; echo done) &
[1] 10080
~/learn $ echo waiting
waiting
~/learn $ done
[1]+ Done ( sleep 5; echo done )
~/learn $
通常、バッシュはコマンドが完了するのを待ってからコマンドプロンプトを表示し、入力を許可します。しかし、コマンドラインの末尾に&を付けると、バッシュは待たずにコマンドを別のプロセスで実行します。上記の~/learn $ echo waitingでは、echo waitingを入力しました。しかし、~/learn $ doneでは、doneを入力しませんでした。これは5秒後にecho doneによって生成されました。[1] 10080はプロセスが開始されたことを示し、[1]+ Done ( sleep 5; echo done )はプロセスが終了したことを示します。
これは、コマンドが終了するのを待ちたくない場合に便利です。Windowsでは次のように考えてみてください:
~ $ notepad
ここでは、バッシュがこのコマンドの終了を待っているため、ノートパッドを終了するまでコマンドプロンプトを再び取得できません。したがって、代わりに次を行います:
~ $ notepad &
[1] 2010
~ $
これでノートパッドが実行され、バッシュを続けて使用できます。
test.shというファイルを作成し、次の内容を含めます:
echo this is a shell script
次にバッシュから:
~/learn $ cat test.sh
echo this is a shell script
~/learn $ ./test.sh
-bash: ./test.sh: Permission denied
~/learn $ ls -F test.sh
test.sh
~/learn $ chmod +x test.sh
~/learn $ ls -F test.sh
test.sh*
~/learn $ ./test.sh
this is a shell script
~/learn $
chmod +x fileはfileを実行可能にして実行できるようにします。次にtest.shを編集します
~/learn $ # edit test.sh
~/learn $ cat test.sh
nonsense
echo this is a shell script
~/learn $ ./test.sh
./test.sh: line 1: nonsense: command not found
this is a shell script
~/learn $ # edit test.sh
~/learn $ cat test.sh
set -e
nonsense
echo this is a shell script
~/learn $ ./test.sh
./test.sh: line 2: nonsense: command not found
~/learn $
デフォルトでは、スクリプトはエラー後も実行を続けます。長いスクリプトでは、エラー後にスクリプトを終了させたいです。set -eはこれを行います。help setを参照してください。
~/learn $ X=some
~/learn $ echo $X
some
~/learn $ echo $Xthing
~/learn $ echo ${X}thing
something
~/learn $ # edit test.sh
~/learn $ cat test.sh
echo "\$* = $*"
echo "\$# = $#"
echo "\$0 = $0"
echo "\$1 = $1"
echo "\$2 = $2"
echo "\$3 = $3"
echo "\$4 = $4"
echo "\$14 = $14"
echo "\${14} = ${14}"
echo "\$@ = $@"
./count.sh "$*"
./count.sh "$@"
~/learn $ ./test.sh a b "c d"
$* = a b c d
$# = 3
$0 = ./test.sh
$1 = a
$2 = b
$3 = c d
$4 =
$14 = a4
${14} =
$@ = a b c d
1
3
~/learn $ cat count.sh
echo $#
~/learn $
バッシュスクリプトには特別に定義された変数があります。$*と$@の違いは微妙で、通常は$*を使用します。$*はすべての引数を1つの文字列として返し、$@は引数を個別に返しますが、この違いはほとんど影響を与えません。
~/learn $ X=value
~/learn $ echo $X
value
~/learn $ # edit test.sh
~/learn $ cat test.sh
echo "\$X = $X"
~/learn $ ./test.sh
$X =
~/learn $ export X
~/learn $ ./test.sh
$X = value
変数は現在のシェルで定義されます。シェルスクリプトは独自のシェルで実行されます。したがって、デフォルトでは、ターミナル/親シェルで定義された変数を参照しません。export varはvarを子プロセスで利用可能にします。つまり、シェルスクリプトで利用可能にします。.bash_profileでexport PATHを行うことをお勧めします。これにより、PATHがスクリプトで利用可能になります。
~/learn $ X=terminal
~/learn $ echo $X
terminal
~/learn $ # edit test.sh
~/learn $ cat test.sh
X=script
export X
~/learn $ ./test.sh
~/learn $ echo $X
terminal
~/learn $ . test.sh
~/learn $ echo $X
script
変数を親から子にエクスポートできますが、子から親にはできません。. scriptはファイルscriptのテキストを現在のシェルに含めます。この場合、別のシェルで実行されません。これが、スクリプトがターミナルシェルで変数を設定する唯一の方法です。
~/learn $ pwd
/Users/fschmidt/learn
~/learn $ # edit test.sh
~/learn $ cat test.sh
cd ~
~/learn $ ./test.sh
~/learn $ pwd
/Users/fschmidt/learn
~/learn $ . test.sh
~ $ pwd
/Users/fschmidt
~ $ cd learn
~/learn $
これは./scriptと. scriptの違いを示しています。
~/learn $ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/fschmidt/Dropbox/bin:/Users/fschmidt/hg/luan/scripts:/usr/local/opt/postgresql@9.5/bin:/Applications/Sublime Text.app/Contents/SharedSupport/bin
~/learn $ echo ~/Dropbox/bin
/Users/fschmidt/Dropbox/bin
~/learn $ ls -F ~/Dropbox/bin/e
/Users/fschmidt/Dropbox/bin/e*
~/learn $ cat ~/Dropbox/bin/e
open -a 'Sublime Text' $*
~/learn $ e test.sh
~/learn $
便利なスクリプトを書くときは、それらをディレクトリに入れ、そのディレクトリをPATHに追加します。私は~/Dropbox/binを使用しており、そこにeという名前のスクリプトがあります。これにより、コマンドラインからファイルを編集できます。e test.shはコマンドラインからtest.shを編集できます。
バッシュはスクリプトを探すときに、明示的なパスを指定しない限り、PATH内のスクリプトのみを探します。
~/learn $ # edit test.sh
~/learn $ cat test.sh
echo this is a shell script
~/learn $ test.sh
-bash: test.sh: command not found
~/learn $ ./test.sh
this is a shell script
~/learn $
test.shを単独で呼び出すと失敗します。これはPATHにないためです。しかし、./test.shは明示的なパスであるため機能します。
ここにWord DOCXファイルを解凍するundocx.shというより高度なスクリプトがあります。
#!/bin/bash
set -e
if [ $# -ne 1 ]; then
echo "usage: $0 filename"
exit 1
fi
FILE="$1"
NEWDIR=$(basename $FILE .docx)
mkdir $NEWDIR
unzip $FILE -d $NEWDIR
export XMLLINT_INDENT=$'\t'
for file in $(find $NEWDIR -name "*.xml" -o -name "*.rels"); do
mv "$file" temp.xml
xmllint --format temp.xml >"$file"
done
rm temp.xml
バッシュは通常の機能を含む完全なプログラミング言語です。私のスクリプトのいくつかのコマンドはmanでよく説明されていますが、いくつかはそうではありません。特にifとforのドキュメントは不十分です。このような場合、ChatGPTに次のように尋ねることをお勧めします:
Please explain the Bash "if" statement.
Please explain the Bash "for" statement.
ChatGPTはバッシュをよく知っています。ChatGPTに詳細を説明してもらうことを信頼していますが、コアコンセプトを説明することは信頼していません。Googleを試すこともできますが、ChatGPTは現代のプログラマーよりも優れています。
バッシュの使用の少なくとも90%は、ターミナルに入力する単純なコマンドです。できるだけバッシュを使用し、GUIを使用する代わりにバッシュを使用して練習してください。システム管理者にならない限り、高度なスクリプトをあまり使用しません。しかし、コアの基本をしっかりと理解していれば、必要に応じて高度なスクリプトを読み書きする方法を理解できるはずです。