1

Can someone explain to me what's going on here?

Script:

#!/bin/sh
SKIP="unity-launcher|unity-panel|unity-dash|Hud|XdndCollectionWindowImp|Desktop"
WINS=()
wmctrl -l | grep -Ev " (${SKIP})" | cut -d \  -f 1 | while read window; do
    WINS=( ${WINS[@]} $window )
    echo "Found window: $window; New size: ${#WINS[@]}"
done
echo "Total window count: ${#WINS[@]}"
echo "Window IDs:"
for i in "${WINS[@]}"; do
    echo "  $i"
done

Output:

Found window: 0x0380000a; New size: 1
Found window: 0x038002ae; New size: 2
Found window: 0x03800a33; New size: 3
Found window: 0x03000001; New size: 4
Found window: 0x04c00004; New size: 5
Total window count: 0
Window IDs:

Expected:

Found window: 0x0380000a; New size: 1
Found window: 0x038002ae; New size: 2
Found window: 0x03800a33; New size: 3
Found window: 0x03000001; New size: 4
Found window: 0x04c00004; New size: 5
Total window count: 5
Window IDs:
    0x0380000a
    0x038002ae
    0x03800a33
    0x03000001
    0x04c00004

Essentially, at the end of the while loop, the WINS array is getting reset somehow, and I have no idea why. Is there some weird scoping thing in bash I'm unaware of?

Fordi
  • 502

1 Answers1

5

Pipes create subshells, and these subshells can't change the values in the processes above them. Try:

while read window; do
    WINS=( ${WINS[@]} $window )
    echo "Found window: $window; New size: ${#WINS[@]}"
done < <(wmctrl -l | grep -Ev " (${SKIP})" | cut -d \  -f 1)
  • You missed a space between the '<'s, but yeah you've the right answer. I was about to post the same myself after I'd done some research, but you got more brevity than I did. For others: The feature Jeff is using here is called 'process substitution'. What's going on is that, in my piped example, the entire loop runs in a subprocess; using process substitution, the parenthesized list runs as a subshell in parallel with the loop, while the loop itself remains in the main process; the output of the pipe list becomes stdin for the preceding command. – Fordi Aug 04 '16 at 15:20
  • Corrected. Thx. –  Aug 04 '16 at 15:23
  • Note that ksh88 and ksh93 (but not pdksh), and zsh in some modes would run the last part of the pipe in the current shell; echo hello | read a acts differently in the different shells. – Stephen Harris Aug 04 '16 at 15:57