开发者

Bash: set array within braces in a while loop? (sub-shell problem)

I'm having problems getting a variable "${Error[*]}", which is a regular indexe开发者_Go百科d array, to stay set from the time it's declared until it's checked. It seems to me that a sub-shell must be launched so the declaration doesn't stick. I didn't think sub-shells were opened when using braces { stuff...; }. I want to know how to get my variable, Error to stick in the case I'm trying to write up. Here's a sample of my script:

TestFunction () {
    unset Error
    local archive="$1" extlist="$2" && local ext="${archive##*.}"
    shopt -s nocasematch
    local -i run=0
    while [[ "$run" == 0 || -n "${Error[run]}" ]]; do
        (( run++ ))
        local IFS=$'\n\r\t '
        if [[ ! "${Error[*]}" =~ 'cpio' && "$ext" =~ ^(pax|cpio|cpgz|igz|ipa|cab)$ && -n "$(which 'cpio')" ]]; then

    ## Try to cpio the archive. Since cpio cannot handle '.cab' archive, I want to declare an Error ##
            { cpio -ti --quiet <"$archive" 2>'/dev/null' || local -a Error[run]='cpio'; } | grep -Ei '$extlist'
        elif [[ ! "${Error[*]}" =~ 'zipinfo' && "$ext" =~ ^(zip|[jw]ar|ipa|cab)$ && -n "$(which 'unzip')" ]]; then

    ## If cpio fails, then try zipinfo, or unzip on the next run through the loop... ##
            if which 'zipinfo' &>'/dev/null'; then
                { zipinfo -1 "$archive" 2>'/dev/null' || local -a Error[run]='zipinfo'; } | grep -Ei "$scanlist"
            elif which 'unzip' &>'/dev/null'; then
                { unzip -lqq "$archive" 2>'/dev/null' || local -a Error[run]='unzip'; } | gsed -re '/^ +[0-9]+/!d;s|^ +[0-9]+ +[0-9-]+ [0-9:]+ +||' | grep -Ei "$exlist"
            fi
    ## many more elifs... ##
        fi
    done
    shopt -u nocasematch
    return 0
}

Archives='\.(gnutar|7-zip|lharc|toast|7zip|boz|bzi?p2?|cpgz|cpio|gtar|g?z(ip)?|lzma(86)?|t[bg]z2?|ar[cgjk]|bz[2a]?|cb[7rz]|cdr|deb|[dt]lz|dmg|exe|fbz|fgz|gz[aip]|igz|img|iso|lh[az]|lz[hmswx]?|mgz|mpv|mpz|pax|piz|pka|[jrtwx]ar|rpm|s?7-?z|sitx?|m?pkg|sfx|nz|xz)$'
IFS=$'\n'
declare -a List=($(TestFunction '/Users/aesthir/Programming/│My Projects│/Swipe Master/Test Folder/SDKSetup.cab' "$Archives"))
IFS=$' \t\n'

xtrace output:

  〔xtrace〕 unset Error
  〔xtrace〕 local 'archive=/Users/aesthir/Programming/│My Projects│/Swipe Master/Test Folder/SDKSetup.cab' 'extlist=\.(gnutar|7-zip|lharc|toast|7zip|boz|bzi?p2?|cpgz|cpio|gtar|g?z(ip)?|lzma(86)?|t[bg]z2?|ar[cgjk]|bz[2a]?|cb[7rz]|cdr|deb|[dt]lz|dmg|exe|fbz|fgz|gz[aip]|igz|img|iso|lh[az]|lz[hmswx]?|mgz|mpv|mpz|pax|piz|pka|[jrtwx]ar|rpm|s?7-?z|sitx?|m?pkg|sfx|nz|xz)$'
  〔xtrace〕 local ext=cab
  〔xtrace〕 shopt -s nocasematch
  〔xtrace〕 local -i run=0
  〔xtrace〕 [[ 0 == 0 ]]
  〔xtrace〕 ((  run++  ))
  〔xtrace〕 local 'IFS=
'
  〔xtrace〕 [[ ! '' =~ cpio ]]
  〔xtrace〕 [[ cab =~ ^(pax|cpio|cpgz|igz|ipa|cab)$ ]]
  〔xtrace〕 which cpio
  〔xtrace〕 [[ -n /usr/bin/cpio ]]
  〔xtrace〕 grep -Ei '$extlist'
  〔xtrace〕 cpio -ti --quiet
  〔xtrace〕 local -a 'Error[run]=cpio'
  〔xtrace〕 [[ 1 == 0 ]]
  〔xtrace〕 [[ -n '' ]]          ## <—— Problem is here... when checking "${Error[run]}", it's unset ##
  〔xtrace〕 shopt -u nocasematch
  〔xtrace〕 return 0


Now obviously I know cpio, zipinfo, and unzip cannot handle cab files... I put 'cab' in the extension list on purpose to cause an error.

I want to stay in TestFunction and keep looping with different archivers until a success (file list is dumped, which cabextract would gladly do in this case) without repeating an already failed archiver.

Finally, since this works fine...

TestFunction () {
    unset Error
    local archive="$1" extlist="$2" && local ext="${archive##*.}"
    local -i run=0
    while [[ "$run" == 0 || -n "${Error[run]}" ]]; do
        (( run++ ))
        local IFS=$'\n\r\t '
        if [[ ! "${Error[*]}" =~ 'cpio' && "$ext" =~ ^(pax|cpio|cpgz|igz|ipa|cab)$ && -n "$(which 'cpio')" ]]; then
            cpio -ti --quiet <"$archive" 2>'/dev/null' || local -a Error[run]='cpio'
        fi
    done
    shopt -u nocasematch
    return 0
}

... I have to assume the problem is the braces because I want the results grep'd right away. However, I need those braces there because I don't want Error[run] to be set if grep turns up no results, only if cpio fails. I dont want to grep outside TestFunction for other reasons (I would have to do a complete re-write).

Any quick solution to this without massive rewriting? Maybe echo 'cpio' to some fd and read -u6ing it somehow?

I'd much prefer not to have to set an array to the file list and then for loop | grep through every file as it would really slow things down.


The problem is not the braces, but the pipe. Because you're using a pipe, the assignment to Error[run] is happening in a subshell, so that assignment disappears when the subshell exits.

Change:

{ cpio -ti --quiet <"$archive" 2>'/dev/null' || local -a Error[run]='cpio'; } | grep -Ei '$extlist'

to:

cpio -ti --quiet <"$archive" 2>'/dev/null' | grep -Ei "$extlist"
[[ ${PIPESTATUS[0]} -ne 0 ]] && Error[run]='cpio'

(btw, need double quotes in the grep part)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜