Bash

嘗試使用 while read line 和 echo 進行 bash 大括號擴展(離散數字),不會擴展

  • April 29, 2020

我將簡要介紹我所面臨的問題。我正在嘗試編寫的實際應用程序有很多部分,所以我嘗試用一個小例子來隔離我遇到的部分。我的情況涉及文件 ID 號,類似於從目錄或其他東西解析的學生 ID 號的假設列表。


我從什麼開始

為了嘗試並利用bash 大括號擴展,已解析學生目錄之類的內容以提供以下內容:

$ cat student_id_potential_expansions
0000{11,24}
0001{32,38,81}
0002{02,80,81,89,97}

期望的行為

我想使用這個輸入來創建我自己的(Bourne Again)shell 腳本,它看起來像下面這樣。

#!/bin/bash

get_transcripts_for_id.sh -id 000011
get_transcripts_for_id.sh -id 000024
get_transcripts_for_id.sh -id 000132
get_transcripts_for_id.sh -id 000133
get_transcripts_for_id.sh -id 000181
get_transcripts_for_id.sh -id 000202
...

等等

我之前不太擔心命令;我想我會用一個

awk '{print "get_transcripts_for_id.sh -id" $0}' student_id_list > my_shell_script.sh

或一個

sed 's/^\(.*\)$/get_transcripts_for_id.sh -id \1/' student_id_list > my_shell_script.sh

現在,我只是想student_id_list從潛在擴展列表中獲取 id 編號列表(從上面)。

我想我知道基本問題,我只是不知道如何解決它。我可以得到我想要的列表片段,如下所示:

$ echo 0000{11,24} | tr ' ' '\n'
000011
000024

$ echo 0001{32,38,81} | tr ' ' '\n'
000132
000138
000181

$ echo 0002{02,80,81,89,97} | tr ' ' '\n'
000202
000280
000281
000289
000297

但是,我第一個想到的命令並沒有擴展內容

# doesn't work, as you can see
$ while read -r line; do echo $line; done < student_id_potential_expansions
0000{11,24}
0001{32,38,81}
0002{02,80,81,89,97}

不想只是複制/粘貼每一行。我已經獲得了成百上千的潛在可擴展行 - 這些是由不知道在哪裡的其他人解析的,我無法訪問從記錄中進行原始解析的人。另外,我的每一行都更像

0000009{882,739,861,014,952,611,862,935,976,916,080,697,323,843,840,487,517,407,256,756,374,682,162,930,758,157,770,505,867,233,198,131,917,848,613,247,961,261,616,392,876,747,873,148,844,849,280,626,817,819,174,771,172,284,217,200,018,624,418,292,642,529,755,855,647,317,881,962,975,237,635,805,298,835,053}

我的理解

來自 bash 手冊,第 3.5.1 節

大括號擴展是一種可以生成任意字元串的機制。

格式正確的大括號展開必須包含不帶引號的左大括號和右大括號,以及至少一個不帶引號的逗號或有效的序列表達式。任何格式不正確的大括號擴展都保持不變。

據我了解,當我閱讀每一行時,我基本上是在給一個字元串提供給 while 循環內的任何內容,即在上面的範例中,我首先給出$lineas "0000{11,24}"(注意引號)。進一步進行,就好像我第一次通過循環導致以下命令

echo "0000{11,24}"

再一次注意引號,即使它們不在$line我的while循環中。為了看看這是否一致,我執行了以下命令並得到了以下結果:

$ echo "0000{11,24}"
0000{11,24}

$ echo "0001{32,38,81}"
0001{32,38,81}

$ echo "0002{02,80,81,89,97}"
0002{02,80,81,89,97}

因此,據我所知,所有的大括號 ({}) 和逗號都被引用了——bash 手冊中所說的會導致無效的擴展語句。那麼,主要問題是,我如何取消引用或取消字元串化$line我讀到的每個內容?


我的嘗試/研究

冒著使這篇文章太長的風險,這裡有一些我所做的嘗試。

瀏覽這篇SO 文章,我很確定我的問題不是單詞分離問題。

這個答案這個評論,特別是提到

bash{}擴展發生在$擴展之前,所以我認為除了使用eval或其他一些技巧來導致兩次通過表達式之外,沒有其他方法可以做到這一點。

讓我想到嘗試

$ while read -r line; do eval "$line"; done < student_id_potential_expansions
bash: 000011: command not found
bash: 000132: command not found
bash: 000202: command not found

$ while read -r line; do echo $(eval "$line"); done < student_id_potential_expansions
bash: 000011: command not found

bash: 000132: command not found

bash: 000202: command not found

在 shell 嘗試執行(評估)命令之前,似乎只進行了第一次擴展。

我還嘗試了以下命令,查看此處的評論。

$ while read -r line; do expansions=$line; echo $expansions; done < trying_inner_echo.txt
$(echo 0000{11,24})
$(echo 0001{32,38,81})
$(echo 0002{02,80,81,89,97})

$ while read -r line; do echo $line; done < trying_inner_echo.txt
$(echo 0000{11,24})
$(echo 0001{32,38,81})
$(echo 0002{02,80,81,89,97})

$ while read -r line; do threads=${line}; exec $threads; done < trying_inner_echo.efl
bash: exec: $(echo: not found
bash: exec: $(echo: not found
bash: exec: $(echo: not found

$ while read -r line; do threads=${line}; exec "${threads}"; done < trying_inner_echo.txt
bash: exec: $(echo 0000{11,24}): not found
bash: exec: $(echo 0001{32,38,81}): not found
bash: exec: $(echo 0002{02,80,81,89,97}): not found

其他一些嘗試:

$ while read -r line; do echo "$line"; done < student_id_potential_expansions
$ while read -r line; do echo "$(echo $line)"; done < student_id_potential_expansions

系統資訊

$ uname -a
CYGWIN_NT-10.0 C-D-ENG-E-INT3 2.11.2(0.329/5/3) 2018-11-08 14:34 x86_64 Cygwin

$ bash --version
GNU bash, version 4.4.12(3)-release (x86_64-unknown-cygwin)
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

$ systeminfo | sed -n 's/^OS\ *//p'
Name:                   Microsoft Windows 10 Enterprise
Version:                10.0.17134 N/A Build 17134
Manufacturer:           Microsoft Corporation
Configuration:          Member Workstation
Build Type:             Multiprocessor Free

我用了什麼(感謝@EchoMike444)

@EchoMike444 的答案是絕對正確的,讓我到達了我需要去的地方,因此這是公認的答案。由於我使用的內容與答案中出現的內容略有不同,因此我將把它放在這裡(而不是在答案的評論中)。來自評論:

謝謝你把我帶到那裡。當我使用

while read -r line; do 
 eval "echo $line"  ## RIGHT!
done &lt; student_id_potential_expansions | tr ' ' '\n' 

我得到了我正在尋找的輸出。我需要幫助才能到達那裡echo $(eval "$line") # wrong!

訣竅是使用eval

一個非常小的例子:

( echo '000{90,91}' ; echo '002{10,11}' ; echo '110{50,51}' ) | \
      while read L ; do eval "echo $L" | tr ' ' '\n'  ; done

引用自:https://unix.stackexchange.com/questions/551055