簡體   English   中英

使用Java中的空參數執行shell腳本

[英]Executing a shell script with an empty argument from Java

我有bash腳本,然后接受幾個參數測試並運行命令,如下所示

call script => /bin/bash myscript arg1 arg2 arg3 arg4
comm called => command -a arg1 -b arg2 -c arg3 -d arg4

如果參數為空,則不調用該選項。

call script => /bin/bash myscript arg1 arg2 '' arg4
comm called => command -a arg1 -b arg2 -d arg4

我可以通過在腳本中使用以下行來測試參數來實現這一點

if test "${arg3:-t}" != "t" ; then

從提示符調用時,此腳本的工作方式類似於魅力。 即使我將''with“替換為空參數也能正常工作。

當我使用exec從java調用此腳本時,這開始失敗。

Process p = Runtime.getRuntime().exec("/bin/bash myscript arg1 arg2 '' arg4");
expected command => command -a arg1 -b arg2 -d arg4 (as in above example)
actual command   => command -a arg1 -b arg2 -c '' -d arg4

我無法理解為什么會發生這種情況。 問題出在哪兒? 在shell腳本中或從java執行命令的方式?

怎么解決這個問題?

基本的問題是java根本不會做任何事情來解析你給它的字符串並將它很好地分解為參數。

簡短的回答是使用Runtime.execString []版本:

Runtime.getRuntime().exec(
    new String[] {"/bin/bash", "myscript", "arg1", "arg2", "",  "arg4"});

如果你有其他地方參數解析比那更復雜,你可以將字符串的解析傳遞給bash,並執行:

Runtime.getRuntime().exec(
    new String[] {"/bin/bash", "-c", "/bin/bash myscript arg1 arg2 '' arg4"});

如果你正在轉換那些正在做大量復雜重定向的東西,比如2>&1或者設置一個完整的管道,你可能需要這個bash -c技巧。

編輯:

要了解這里發生了什么,你必須意識到當用戶空間代碼告訴內核“使用這些參數加載這個可執行文件並基於它啟動一個進程”(*)時,傳遞給內核的是一個可執行文件,參數的字符串數組(此參數數組的第0個/第一個元素是可執行文件的名稱,除非做了一些奇怪的事情),以及環境的字符串數組。

當看到這一行時bash會做什么:

/bin/bash myscript arg1 arg2 '' arg4

我想是“好吧, /bin/bash不是內置的,所以我正在執行一些東西。讓我們使用我的字符串解析算法將子進程的參數數組放在一起,這些算法知道引用和諸如此類的東西”。 然后Bash確定為新進程傳遞內核的參數是:

  • /bin/bash

  • myscript

  • arg1

  • arg2

  • (空字符串)

  • arg4

現在bash有相當復雜的字符串處理算法,它適用於此處 - 它接受兩種不同的引號,當它發生在字符串之外或雙引號內時它會擴展$VAR ,它會用輸出替換反引號中的子命令,等等

當你調用exec的單字符串版本時,Java不會做任何如此復雜的事情。 它只是創建一個新的StringTokenizer並使用它來分解你將它賦予參數的字符串。 那堂課對報價一無所知; 它將該字符串拆分為:

  • /bin/bash

  • myscript

  • arg1

  • arg2

  • '' (帶有兩個字符的字符串,兩者都是單引號)

  • arg4

Java然后調用execString[]版本。 (好吧,其中一個)

人們通過以下方式挑選尼特的說明:

(*)是的,我故意忽略系統調用forkexecve之間的區別。 假裝他們暫時是一個電話。

試試這個:

ProcessBuilder pb = new ProcessBuilder("/bin/bash", "myscript", "arg1", "arg2", "",  "arg4");
Process proc = pb.start();

您不應該使用Runtime.exec Java 5引入了一個您應該使用的ProcessBuilder 它使您可以更好地控制啟動過程和設置環境。

ProcessBuilder pb = new ProcessBuilder("/bin/bash", "myscript", "arg1", "arg2", "", "arg4");
pb.start();

這樣的事怎么樣?

javaParams=""
[ -n "$1" ] && javaParams="-a '$1'"
[ -n "$2" ] && javaParams="$javaParams -b '$2'"
[ -n "$3" ] && javaParams="$javaParams -c '$3'"
[ -n "$4" ] && javaParams="$javaParams -d '$4'"
command $javaParams

[ -n "$1" ] && javaParams="-a $1"

相當於:

if [ -n "$1" ]
then
    javaParams="-a $1"
fi

-n test查看提供的字符串是否為零長度。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM