Julia 使用倒引號 ` 來運行外部程序:
julia> `echo hello`
`echo hello`
它有以下幾個特性:
Cmd
對象來表示這個命令??梢杂眠@個對象,通過管道將命令連接起來,運行,并進行讀寫libc
的 system
,命令的輸出默認指向 stdout
。fork
和 exec
函數(shù),作為 julia
的直接子進程。下面是運行外部程序的例子:
julia> run(`echo hello`)
hello
hello
是 echo
命令的輸出,它被送到標(biāo)準(zhǔn)輸出。 run
方法本身返回 nothing
。如果外部命令沒有正確運行,將拋出 ErrorException
異常。
使用 readall
讀取命令的輸出:
julia> a=readall(`echo hello`)
"hello\n"
julia> (chomp(a)) == "hello"
true
更普遍的,你可以使用 open
從一個外部命令讀取或者寫到一個外部命令。例如:
julia> open(`less`, "w", STDOUT) do io
for i = 1:1000
println(io, i)
end
end
將文件名賦給變量 file
,將其作為命令的參數(shù)。像在字符串文本中一樣使用 $
做內(nèi)插(詳見 :ref:man-strings
):
julia> file = "/etc/passwd"
"/etc/passwd"
julia> `sort $file`
`sort /etc/passwd`
如果文件名有特殊字符,比如 /Volumes/External HD/data.csv
,會如下顯示:
julia> file = "/Volumes/External HD/data.csv"
"/Volumes/External HD/data.csv"
julia> `sort $file`
`sort '/Volumes/External HD/data.csv'`
文件名被單引號引起來了。Julia 知道 file
會被當(dāng)做一個單變量進行內(nèi)插,它自動把內(nèi)容引了起來。事實上,這也不準(zhǔn)確: file
的值并不會被 shell 解釋,所以不需要真正的引起來;此處把它引起來,只是為了給用戶顯示。下例也可以正常運行:
julia> path = "/Volumes/External HD"
"/Volumes/External HD"
julia> name = "data"
"data"
julia> ext = "csv"
"csv"
julia> `sort $path/$name.$ext`
`sort '/Volumes/External HD/data.csv'`
如果要內(nèi)插多個單詞,應(yīng)使用數(shù)組(或其它可迭代容器):
julia> files = ["/etc/passwd","/Volumes/External HD/data.csv"]
2-element ASCIIString Array:
"/etc/passwd"
"/Volumes/External HD/data.csv"
julia> `grep foo $files`
`grep foo /etc/passwd '/Volumes/External HD/data.csv'`
如果數(shù)組內(nèi)插為 shell 單詞的一部分,Julia 會模仿 shell 的 {a,b,c}
參數(shù)生成的行為:
julia> names = ["foo","bar","baz"]
3-element ASCIIString Array:
"foo"
"bar"
"baz"
julia> `grep xylophone $names.txt`
`grep xylophone foo.txt bar.txt baz.txt`
如果將多個數(shù)組內(nèi)插進同一個單詞,Julia 會模仿 shell 的笛卡爾乘積生成的行為:
julia> names = ["foo","bar","baz"]
3-element ASCIIString Array:
"foo"
"bar"
"baz"
julia> exts = ["aux","log"]
2-element ASCIIString Array:
"aux"
"log"
julia> `rm -f $names.$exts`
`rm -f foo.aux foo.log bar.aux bar.log baz.aux baz.log`
不構(gòu)造臨時數(shù)組對象,直接內(nèi)插文本化數(shù)組:
julia> `rm -rf $["foo","bar","baz","qux"].$["aux","log","pdf"]`
`rm -rf foo.aux foo.log foo.pdf bar.aux bar.log bar.pdf baz.aux baz.log baz.pdf qux.aux qux.log qux.pdf`
命令復(fù)雜時,有時需要使用引號。來看一個 perl 的命令:
sh$ perl -le '$|=1; for (0..3) { print }'
0
1
2
3
再看個使用雙引號的命令:
sh$ first="A"
sh$ second="B"
sh$ perl -le '$|=1; print for @ARGV' "1: $first" "2: $second"
1: A
2: B
一般來說,Julia 的倒引號語法支持將 shell 命令原封不動的復(fù)制粘貼進來,且轉(zhuǎn)義、引用、內(nèi)插等行為可以原封不動地正常工作。唯一的區(qū)別是,內(nèi)插被集成進了 Julia 中:
julia> `perl -le '$|=1; for (0..3) { print }'`
`perl -le '$|=1; for (0..3) { print }'`
julia> run(ans)
0
1
2
3
julia> first = "A"; second = "B";
julia> `perl -le 'print for @ARGV' "1: $first" "2: $second"`
`perl -le 'print for @ARGV' '1: A' '2: B'`
julia> run(ans)
1: A
2: B
當(dāng)需要在 Julia 中運行 shell 命令時,先試試復(fù)制粘貼。Julia 會先顯示出來命令,可以據(jù)此檢查內(nèi)插是否正確,再去運行命令。
Shell 元字符,如 |
, &
, 及 >
在 Julia 倒引號語法中并是不特殊字符。倒引號中的管道符僅僅是文本化的管道字符 “|” 而已:
julia> run(`echo hello | sort`)
hello | sort
在 Julia 中要想構(gòu)造管道,應(yīng)在 Cmd
間使用 |>
運算符:
julia> run(`echo hello` |> `sort`)
hello
繼續(xù)看個例子:
julia> run(`cut -d: -f3 /etc/passwd` |> `sort -n` |> `tail -n5`)
210
211
212
213
214
它打印 UNIX 系統(tǒng)五個最高級用戶的 ID 。 cut
, sort
和 tail
命令都作為當(dāng)前 julia
進程的直接子進程運行,shell 進程沒有介入。
Julia 自己來設(shè)置管道并連接文件描述符, 這些工作通常由 shell 來完成。也
因此, Julia 可以對子進程實現(xiàn)更好的控制, 也可以實現(xiàn) shell 不能實現(xiàn)的一
些功能. 值得注意的是, |>
僅僅是重定向了 stdout
. 使用 .>
來
重定向 stderr
.
Julia 可以并行運行多個命令:
julia> run(`echo hello` & `echo world`)
world
hello
輸出順序是非確定性的。兩個 echo
進程幾乎同時開始,它們競爭 stdout
描述符的寫操作,這個描述符被兩個進程和 julia
進程所共有。使用管道,可將這些進程的輸出傳遞給其它程序:
julia> run(`echo world` & `echo hello` |> `sort`)
hello
world
來看一個復(fù)雜的使用 Julia 來調(diào)用 perl 命令的例子:
julia> prefixer(prefix, sleep) = `perl -nle '$|=1; print "'$prefix' ", $_; sleep '$sleep';'`
julia> run(`perl -le '$|=1; for(0..9){ print; sleep 1 }'` |> prefixer("A",2) & prefixer("B",2))
A 0
B 1
A 2
B 3
A 4
B 5
A 6
B 7
A 8
B 9
這是一個單生產(chǎn)者雙并發(fā)消費者的經(jīng)典例子:一個 perl
進程生產(chǎn)從 0 至 9 的 10 行數(shù),兩個并行的進程消費這些結(jié)果,其中一個給結(jié)果加前綴 “A”,另一個加前綴 “B”。我們不知道哪個消費者先消費第一行,但一旦開始,兩個進程交替消費這些行。(在 Perl 中設(shè)置 $|=1
,可使打印表達式先清空 stdout
句柄;否則輸出會被緩存并立即打印給管道,結(jié)果將只有一個消費者進程在讀取。)
再看個更復(fù)雜的多步的生產(chǎn)者-消費者的例子:
julia> run(`perl -le '$|=1; for(0..9){ print; sleep 1 }'` |>
prefixer("X",3) & prefixer("Y",3) & prefixer("Z",3) |>
prefixer("A",2) & prefixer("B",2))
B Y 0
A Z 1
B X 2
A Y 3
B Z 4
A X 5
B Y 6
A Z 7
B X 8
A Y 9
此例和前例類似,單有消費者分兩步,且兩步的延遲不同。
強烈建議你親手試試這些例子,看看它們是如何運行的。
更多建議: