Bash中,在for循环中使用2个数组,然后在for循环内部使用2个数组。

huangapple go评论96阅读模式
英文:

Bash, using 2 arrays in for loop and then 2 arrays inside the for loop

问题

  1. 对于第一个目录结构:
  2. DIRS1="foo bar live1 live2"
  3. SRCDIR1="/mnt/nas/stage/live/live/"
  4. 对于第二个目录结构:
  5. DIRS2="test1 test2"
  6. SRCDIR2="/mnt/nas/stage/test/test/"
  7. # 遍历第一个目录结构
  8. for dirname in ${DIRS1}; do
  9. # 转到源同步目录
  10. cd "${SRCDIR1}/${dirname}"
  11. # 获取最新的mux文件名
  12. filename=$(ls -t *.mux 2>/dev/null | head -n 1)
  13. echo $filename
  14. ### 这里有额外的代码处理文件,使用了 $dirname
  15. done
  16. # 遍历第二个目录结构
  17. for dirname in ${DIRS2}; do
  18. # 转到源同步目录
  19. cd "${SRCDIR2}/${dirname}"
  20. # 获取最新的mux文件名
  21. filename=$(ls -t *.mux 2>/dev/null | head -n 1)
  22. echo $filename
  23. ### 这里有额外的代码处理文件,使用了 $dirname
  24. done
英文:

I have a piece of code that will echo the file in a folders that is described in two variables.
This is fine and works.
However, i now want to list the files on another folder structure. However i cannot get it to list.

  1. DIRS="foo bar live1 live2"
  2. SRCDIR="/mnt/nas/stage/live/live/"
  3. # iterate over all directories
  4. for dirname in "${DIRS}"; do
  5. # go to source sync directory
  6. cd "${SRCDIR}/${dirname}"
  7. # get latest mux file name
  8. filename=`ls -t *.mux 2>/dev/null |head -n 1`
  9. echo $filename
  10. ###There is additional code to work on the files here that uses $dirname
  11. done

I would then like to add another directory structure;

  1. DIRS1="foo bar live1 live2"
  2. SRCDIR1="/mnt/nas/stage/live/live/"
  3. DIRS2="test1 test2"
  4. SRCDIR2="/mnt/nas/stage/test/test/"

i have got to
for dirname in ${DIRS1} ${DIRS2}
This works as i get a No such file or directory error,
but then I cannot work out to change my CD variable to SRCDIR2, i did think to put another for loop

  1. for dirpath in ${SRCDIR1} ${SRCDIR2}
  2. cd "${dirpath}/${dirname}"

However this gives a cartesian response. How can i sperate out the to only get DIR1 looking in SRCDIR1 and DIR2 looking in SRCDIR2?

Looking at other answer, i may need to use an array but cannot see how to put this into the code.

答案1

得分: 2

如果您确定目录名称不包含空格,您可以使用一对数组,例如:

  1. DIRS[1]="foo bar live1 live2"
  2. SRCDIR[1]="/mnt/nas/stage/live/live/"
  3. DIRS[2]="test1 test2"
  4. SRCDIR[2]="/mnt/nas/stage/test/test/"
  5. for ((id=1; id<=2; id++))
  6. do
  7. for dir in ${DIRS[id]} # 不用双引号以便单独处理每个条目
  8. do
  9. echo "${SRCDIR[id]}${dir}"
  10. done
  11. done

这将生成:

  1. /mnt/nas/stage/live/live/foo
  2. /mnt/nas/stage/live/live/bar
  3. /mnt/nas/stage/live/live/live1
  4. /mnt/nas/stage/live/live/live2
  5. /mnt/nas/stage/test/test/test1
  6. /mnt/nas/stage/test/test/test2

如果目录名称可能包含空格,此解决方案(未加引号的${DIRS[id]})不起作用,考虑以下解决方案:

  1. DIRS[1]="foo bar live1 live2 'foo bar live{1,2}'" # 第5个条目包含一对空格
  2. SRCDIR[1]="/mnt/nas/stage/live/live/"
  3. DIRS[2]="test1 test2"
  4. SRCDIR[2]="/mnt/nas/stage/test/test/"
  5. for ((id=1; id<=2; id++))
  6. do
  7. for dir in ${DIRS[id]} # 以空格分隔
  8. do
  9. echo "${SRCDIR[id]}${dir}"
  10. done
  11. done

这将生成:

  1. /mnt/nas/stage/live/live/foo
  2. /mnt/nas/stage/live/live/bar
  3. /mnt/nas/stage/live/live/live1
  4. /mnt/nas/stage/live/live/live2
  5. /mnt/nas/stage/live/live/'foo
  6. /mnt/nas/stage/live/live/bar
  7. /mnt/nas/stage/live/live/live{1,2}'
  8. /mnt/nas/stage/test/test/test1
  9. /mnt/nas/stage/test/test/test2

如果目录名称可能包含空格,另一种方法是使用硬编码的数组名称和名字引用(declare -n),例如:

  1. DIRS1=("foo bar live1 live2 'foo bar live{1,2}'") # 第5个条目包含一对空格
  2. SRCDIR1="/mnt/nas/stage/live/live/"
  3. DIRS2=("test1 test2")
  4. SRCDIR2="/mnt/nas/stage/test/test/"
  5. for ((id=1; id<=2; id++))
  6. do
  7. declare -n dirs="DIRS${id}" # 名字引用;第一次dirs是指向DIRS1的指针
  8. declare -n srcdir="SRCDIR${id}" # 名字引用;第一次srcdir是指向SRCDIR1的指针
  9. for dir in "${dirs[@]}"
  10. do
  11. echo "${srcdir}${dir}"
  12. done
  13. done

这将生成:

  1. /mnt/nas/stage/live/live/foo
  2. /mnt/nas/stage/live/live/bar
  3. /mnt/nas/stage/live/live/live1
  4. /mnt/nas/stage/live/live/live2
  5. /mnt/nas/stage/live/live/foo bar live{1,2} # 第5个条目保留空格
  6. /mnt/nas/stage/test/test/test1
  7. /mnt/nas/stage/test/test/test2

请注意:名字引用需要bash 4.2+。最佳解决方案是使用多维数组(例如,DIRS[1,1]="foo"),但不幸的是,bash 不支持多维数组。可以通过索引串联(例如,DIRS[1-1]="foo")来模拟多维数组,但所需的代码会变得有些混乱。如果感兴趣,可以自行研究这个想法。

英文:

If you know for a fact directory names do not contain whitespace you could use a pair of arrays, eg:

  1. DIRS[1]=&quot;foo bar live1 live2&quot;
  2. SRCDIR[1]=&quot;/mnt/nas/stage/live/live/&quot;
  3. DIRS[2]=&quot;test1 test2&quot;
  4. SRCDIR[2]=&quot;/mnt/nas/stage/test/test/&quot;
  5. for ((id=1; id&lt;=2; id++))
  6. do
  7. for dir in ${DIRS[id]} # no double quotes so that each entry is processed separately
  8. do
  9. echo &quot;${SRCDIR[id]}${dir}&quot;
  10. done
  11. done

This generates:

  1. /mnt/nas/stage/live/live/foo
  2. /mnt/nas/stage/live/live/bar
  3. /mnt/nas/stage/live/live/live1
  4. /mnt/nas/stage/live/live/live2
  5. /mnt/nas/stage/test/test/test1
  6. /mnt/nas/stage/test/test/test2

If directory names could contain whitespace this solution (unquoted ${DIRS[id]}) does not work, consider:

  1. DIRS[1]=&quot;foo bar live1 live2 &#39;foo bar live{1,2}&#39;&quot; # 5th entry contains a pair of spaces
  2. SRCDIR[1]=&quot;/mnt/nas/stage/live/live/&quot;
  3. DIRS[2]=&quot;test1 test2&quot;
  4. SRCDIR[2]=&quot;/mnt/nas/stage/test/test/&quot;
  5. for ((id=1; id&lt;=2; id++))
  6. do
  7. for dir in ${DIRS[id]} # split on whitespace
  8. do
  9. echo &quot;${SRCDIR[id]}${dir}&quot;
  10. done
  11. done

This generates:

  1. /mnt/nas/stage/live/live/foo
  2. /mnt/nas/stage/live/live/bar
  3. /mnt/nas/stage/live/live/live1
  4. /mnt/nas/stage/live/live/live2
  5. /mnt/nas/stage/live/live/&#39;foo # 5th entry
  6. /mnt/nas/stage/live/live/bar # broken into
  7. /mnt/nas/stage/live/live/live{1,2}&#39; # 3 parts
  8. /mnt/nas/stage/test/test/test1
  9. /mnt/nas/stage/test/test/test2

If directory names can contain white space then another approach would be to use hard-coded array names and namerefs (declare -n), eg:

  1. DIRS1=(foo bar live1 live2 &#39;foo bar live{1,2}&#39;) # 5th entry contains a pair of spaces
  2. SRCDIR1=&quot;/mnt/nas/stage/live/live/&quot;
  3. DIRS2=(test1 test2)
  4. SRCDIR2=&quot;/mnt/nas/stage/test/test/&quot;
  5. for ((id=1; id&lt;=2; id++))
  6. do
  7. declare -n dirs=&quot;DIRS${id}&quot; # nameref; 1st time dirs is a pointer to DIRS1
  8. declare -n srcdir=&quot;SRCDIR${id}&quot; # nameref; 1st time srcdir is a pointer to SRCDIR1
  9. for dir in &quot;${dirs[@]}&quot;
  10. do
  11. echo &quot;${srcdir}${dir}&quot;
  12. done
  13. done

NOTE: namerefs require bash 4.2+

This generates:

  1. /mnt/nas/stage/live/live/foo
  2. /mnt/nas/stage/live/live/bar
  3. /mnt/nas/stage/live/live/live1
  4. /mnt/nas/stage/live/live/live2
  5. /mnt/nas/stage/live/live/foo bar live{1,2} # 5th entry maintains whitespace
  6. /mnt/nas/stage/test/test/test1
  7. /mnt/nas/stage/test/test/test2

As mentioned in comments, the ideal solution would be a pair of multi-dimensional arrays (eg, DIRS[1,1]=&quot;foo&quot;); unfortunately bash does not support multi-dimensional arrays.

Yes, it is possible to simulate a multi-dimensional array via concatenation of indices (eg, DIRS[1-1]=&quot;foo&quot;) but the necessary code gets a bit messy. I'll leave it to OP to research this idea if it's of interest ...

答案2

得分: 0

给定Bash数组和关联数组的限制,以及使用变量间接方式可能出现的许多陷阱,我会考虑使用类似这样的Shellcheck-干净的代码:

  1. #! /bin/bash -p
  2. # SRCDIR DIRS
  3. # -------------------------- -----------------------
  4. srcdir_dirs=( /mnt/nas/stage/live/live foo bar live1 live2 ''
  5. /mnt/nas/stage/test/test 'test 1' 'test 2' '' )
  6. srcdir='' dirname=''
  7. for d in "${srcdir_dirs[@]}"; do
  8. if [[ -z $d ]]; then
  9. srcdir=''
  10. elif [[ -z $srcdir ]]; then
  11. srcdir=$d
  12. else
  13. dirname=$d
  14. printf '%s/%s\n' "$srcdir" "$dirname"
  15. # ...
  16. fi
  17. done
  • 这适用于除古老版本的Bash以外的所有版本(已测试Bash 3和Bash 5)。
  • 我已将test1test2替换为test 1test 2,以演示包含空格的名称不会引起问题。即使名称中包含换行符也是可以的。
  • srcdir_dirs中的空字符串('')条目用于界定定义源目录和多个子目录的条目组。这是安全的,因为空字符串不是有效的文件或目录名称或路径。
  • srcdir_dirs数组可以轻松扩展以处理三个或更多源目录。
  • 有关为什么使用printf而不是echo的解释,请参见接受的并且很好的答案为什么printf比echo好?
英文:

Given the limitations of Bash arrays and associative arrays, and the many pitfalls associated with using variables indirectly, I'd consider using something like this Shellcheck-clean code:

  1. #! /bin/bash -p
  2. # SRCDIR DIRS
  3. # -------------------------- -----------------------
  4. srcdir_dirs=( /mnt/nas/stage/live/live foo bar live1 live2 &#39;&#39;
  5. /mnt/nas/stage/test/test &#39;test 1&#39; &#39;test 2&#39; &#39;&#39; )
  6. srcdir=&#39;&#39; dirname=&#39;&#39;
  7. for d in &quot;${srcdir_dirs[@]}&quot;; do
  8. if [[ -z $d ]]; then
  9. srcdir=&#39;&#39;
  10. elif [[ -z $srcdir ]]; then
  11. srcdir=$d
  12. else
  13. dirname=$d
  14. printf &#39;%s/%s\n&#39; &quot;$srcdir&quot; &quot;$dirname&quot;
  15. # ...
  16. fi
  17. done
  • This works with all but ancient versions of Bash (tested with Bash 3 and Bash 5).
  • I've replaced test1 and test2 with test 1 and test 2 to demonstrate that names containing whitespace do not cause problems. Even newlines in names would be OK.
  • The empty string (&#39;&#39;) entries in srcdir_dirs delimit groups of entries that define a source directory and multiple subdirectories. This is safe because the empty string is not a valid file or directory name or path.
  • The srcdir_dirs array can easily be extended to handle three or more source directories.
  • See the accepted, and excellent, answer to Why is printf better than echo? for an explanation of why I used printf instead of echo.

huangapple
  • 本文由 发表于 2023年5月26日 01:21:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/76334848.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定