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