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

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

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

问题

对于第一个目录结构:
DIRS1="foo bar live1 live2"
SRCDIR1="/mnt/nas/stage/live/live/"

对于第二个目录结构:
DIRS2="test1 test2"
SRCDIR2="/mnt/nas/stage/test/test/"

# 遍历第一个目录结构
for dirname in ${DIRS1}; do
  # 转到源同步目录
  cd "${SRCDIR1}/${dirname}"
  # 获取最新的mux文件名
  filename=$(ls -t *.mux 2>/dev/null | head -n 1)
  echo $filename

  ### 这里有额外的代码处理文件,使用了 $dirname
done

# 遍历第二个目录结构
for dirname in ${DIRS2}; do
  # 转到源同步目录
  cd "${SRCDIR2}/${dirname}"
  # 获取最新的mux文件名
  filename=$(ls -t *.mux 2>/dev/null | head -n 1)
  echo $filename

  ### 这里有额外的代码处理文件,使用了 $dirname
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.

DIRS="foo bar live1 live2"
SRCDIR="/mnt/nas/stage/live/live/"

# iterate over all directories
for dirname in "${DIRS}"; do
  # go to source sync directory
  cd "${SRCDIR}/${dirname}"
  # get latest mux file name
  filename=`ls -t *.mux 2>/dev/null |head -n 1`
  echo $filename

  ###There is additional code to work on the files here that uses $dirname

done

I would then like to add another directory structure;

DIRS1="foo bar live1 live2"
SRCDIR1="/mnt/nas/stage/live/live/"
DIRS2="test1 test2"
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

for dirpath in ${SRCDIR1} ${SRCDIR2}
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

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

DIRS[1]="foo bar live1 live2"
SRCDIR[1]="/mnt/nas/stage/live/live/"
DIRS[2]="test1 test2"
SRCDIR[2]="/mnt/nas/stage/test/test/"

for ((id=1; id<=2; id++))
do
    for dir in ${DIRS[id]}  # 不用双引号以便单独处理每个条目
    do
        echo "${SRCDIR[id]}${dir}"
    done
done

这将生成:

/mnt/nas/stage/live/live/foo
/mnt/nas/stage/live/live/bar
/mnt/nas/stage/live/live/live1
/mnt/nas/stage/live/live/live2
/mnt/nas/stage/test/test/test1
/mnt/nas/stage/test/test/test2

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

DIRS[1]="foo bar live1 live2 'foo bar live{1,2}'"  # 第5个条目包含一对空格
SRCDIR[1]="/mnt/nas/stage/live/live/"
DIRS[2]="test1 test2"
SRCDIR[2]="/mnt/nas/stage/test/test/"

for ((id=1; id<=2; id++))
do
    for dir in ${DIRS[id]}  # 以空格分隔
    do
        echo "${SRCDIR[id]}${dir}"
    done
done

这将生成:

/mnt/nas/stage/live/live/foo
/mnt/nas/stage/live/live/bar
/mnt/nas/stage/live/live/live1
/mnt/nas/stage/live/live/live2
/mnt/nas/stage/live/live/'foo
/mnt/nas/stage/live/live/bar
/mnt/nas/stage/live/live/live{1,2}'
/mnt/nas/stage/test/test/test1
/mnt/nas/stage/test/test/test2

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

DIRS1=("foo bar live1 live2 'foo bar live{1,2}'")  # 第5个条目包含一对空格
SRCDIR1="/mnt/nas/stage/live/live/"
DIRS2=("test1 test2")
SRCDIR2="/mnt/nas/stage/test/test/"

for ((id=1; id<=2; id++))
do
    declare -n dirs="DIRS${id}"  # 名字引用;第一次dirs是指向DIRS1的指针
    declare -n srcdir="SRCDIR${id}"  # 名字引用;第一次srcdir是指向SRCDIR1的指针

    for dir in "${dirs[@]}"
    do
        echo "${srcdir}${dir}"
    done
done

这将生成:

/mnt/nas/stage/live/live/foo
/mnt/nas/stage/live/live/bar
/mnt/nas/stage/live/live/live1
/mnt/nas/stage/live/live/live2
/mnt/nas/stage/live/live/foo bar live{1,2}  # 第5个条目保留空格
/mnt/nas/stage/test/test/test1
/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:

  DIRS[1]=&quot;foo bar live1 live2&quot;
SRCDIR[1]=&quot;/mnt/nas/stage/live/live/&quot;
  DIRS[2]=&quot;test1 test2&quot;
SRCDIR[2]=&quot;/mnt/nas/stage/test/test/&quot;

for ((id=1; id&lt;=2; id++))
do
    for dir in ${DIRS[id]}                             # no double quotes so that each entry is processed separately
    do
        echo &quot;${SRCDIR[id]}${dir}&quot;
    done
done

This generates:

/mnt/nas/stage/live/live/foo
/mnt/nas/stage/live/live/bar
/mnt/nas/stage/live/live/live1
/mnt/nas/stage/live/live/live2
/mnt/nas/stage/test/test/test1
/mnt/nas/stage/test/test/test2

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

  DIRS[1]=&quot;foo bar live1 live2 &#39;foo bar live{1,2}&#39;&quot;    # 5th entry contains a pair of spaces
SRCDIR[1]=&quot;/mnt/nas/stage/live/live/&quot;
  DIRS[2]=&quot;test1 test2&quot;
SRCDIR[2]=&quot;/mnt/nas/stage/test/test/&quot;

for ((id=1; id&lt;=2; id++))
do
    for dir in ${DIRS[id]}                             # split on whitespace
    do
        echo &quot;${SRCDIR[id]}${dir}&quot;
    done
done

This generates:

/mnt/nas/stage/live/live/foo
/mnt/nas/stage/live/live/bar
/mnt/nas/stage/live/live/live1
/mnt/nas/stage/live/live/live2
/mnt/nas/stage/live/live/&#39;foo                          # 5th entry
/mnt/nas/stage/live/live/bar                           # broken into
/mnt/nas/stage/live/live/live{1,2}&#39;                    # 3 parts
/mnt/nas/stage/test/test/test1
/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:

  DIRS1=(foo bar live1 live2 &#39;foo bar live{1,2}&#39;)      # 5th entry contains a pair of spaces
SRCDIR1=&quot;/mnt/nas/stage/live/live/&quot;
  DIRS2=(test1 test2)
SRCDIR2=&quot;/mnt/nas/stage/test/test/&quot;

for ((id=1; id&lt;=2; id++))
do
    declare -n dirs=&quot;DIRS${id}&quot;                        # nameref; 1st time dirs is a pointer to DIRS1
    declare -n srcdir=&quot;SRCDIR${id}&quot;                    # nameref; 1st time srcdir is a pointer to SRCDIR1

    for dir in &quot;${dirs[@]}&quot;
    do
        echo &quot;${srcdir}${dir}&quot;
    done
done

NOTE: namerefs require bash 4.2+

This generates:

/mnt/nas/stage/live/live/foo
/mnt/nas/stage/live/live/bar
/mnt/nas/stage/live/live/live1
/mnt/nas/stage/live/live/live2
/mnt/nas/stage/live/live/foo bar live{1,2}             # 5th entry maintains whitespace
/mnt/nas/stage/test/test/test1
/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-干净的代码:

#! /bin/bash -p

#               SRCDIR                      DIRS
#               --------------------------  -----------------------
srcdir_dirs=(   /mnt/nas/stage/live/live    foo bar live1 live2 ''
                /mnt/nas/stage/test/test    'test 1' 'test 2' ''    )

srcdir='' dirname=''
for d in "${srcdir_dirs[@]}"; do
    if [[ -z $d ]]; then
        srcdir=''
    elif [[ -z $srcdir ]]; then
        srcdir=$d
    else
        dirname=$d
        printf '%s/%s\n' "$srcdir" "$dirname"
        # ...
    fi
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:

#! /bin/bash -p

#               SRCDIR                      DIRS
#               --------------------------  -----------------------
srcdir_dirs=(   /mnt/nas/stage/live/live    foo bar live1 live2 &#39;&#39;
                /mnt/nas/stage/test/test    &#39;test 1&#39; &#39;test 2&#39; &#39;&#39;    )

srcdir=&#39;&#39; dirname=&#39;&#39;
for d in &quot;${srcdir_dirs[@]}&quot;; do
    if [[ -z $d ]]; then
        srcdir=&#39;&#39;
    elif [[ -z $srcdir ]]; then
        srcdir=$d
    else
        dirname=$d
        printf &#39;%s/%s\n&#39; &quot;$srcdir&quot; &quot;$dirname&quot;
        # ...
    fi
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:

确定