Julia 運(yùn)行外部程序

2018-08-12 21:26 更新

運(yùn)行外部程序

Julia 使用倒引號(hào) ` 來(lái)運(yùn)行外部程序:

    julia> `echo hello`
    `echo hello`

它有以下幾個(gè)特性:

  • 倒引號(hào)并不直接運(yùn)行程序,它構(gòu)造一個(gè) Cmd 對(duì)象來(lái)表示這個(gè)命令??梢杂眠@個(gè)對(duì)象,通過(guò)管道將命令連接起來(lái),運(yùn)行,并進(jìn)行讀寫
  • 命令運(yùn)行時(shí),除非指明, Julia 并不捕獲輸出。它調(diào)用 libcsystem ,命令的輸出默認(rèn)指向 stdout 。
  • 命令運(yùn)行不需要 shell 。 Julia 直接解析命令語(yǔ)法,對(duì)變量?jī)?nèi)插,像 shell 一樣分隔單詞,它遵循 shell 引用語(yǔ)法。命令調(diào)用 forkexec 函數(shù),作為 julia 的直接子進(jìn)程。

下面是運(yùn)行外部程序的例子:

    julia> run(`echo hello`)
    hello

helloecho 命令的輸出,它被送到標(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

內(nèi)插

將文件名賦給變量 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, sorttail 命令都作為當(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)行的。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)