2

Basically I am trying to recursively list sub directories until I hit a directory where there are no more directories to be found. The problem here, is that when I pass the argument into the function, as it is given, the ls command doesn't work. Without the $var in quotations, ls treats the space separated string as multiple arguments.

Why is this happening and how can I avoid it?

#! /bin/bash
function subdir_check(){
    local var="$*"
    ls -F -b "$var" | grep '/' | sed 's/\///g'
}
directory="$1"
if [[ $(subdir_check $directory) != "" ]]
then 
    pick=$(subdir_check $directory | rofi -dmenu -p 'Select Subdirectory')
    directory="${directory}/${pick}"
    while [[ $(subdir_check $directory) != "" ]]
    do
        pick=$(subdir_check $directory | rofi -dmenu -p 'Select Subdirectory')
        directory="${directory}/${pick}"
    done
fi
echo $directory

  • 1
    "Without the $var in quotations, ls treats the space separated string as multiple arguments. Why is this happening and how can I avoid it?" You have just described a fundamental rule for the shell, quoting. It's happening because that is the rule, you can avoid it by quoting your variable, as you basically always should. This script is also riddled with multiple other bad practices but as long as it's not in production please don't let me or anyone else on this site discourage you from continuing to experiment. – jesse_b Sep 07 '19 at 14:51
  • @Jesse_b it isn't really a dupe. The issue here is both quoting and the use of ls with -b. Closing as a dupe won't really help the OP understand why the script is failing nor how to fix it. If you quote all the variables here correctly, it will still fail. – terdon Sep 07 '19 at 15:10
  • I still think it's a duplicate, at least for the subquestion where OP is literally asking "Why does whitespace expand inside an unquotted variable" – jesse_b Sep 07 '19 at 15:43
  • Please clarify why tree can't be used to "recursively list sub directories until I hit a directory where there are no more directories to be found". – agc Sep 08 '19 at 06:20
  • @agc I thought it would difficult to process the output to present it how I want it. Its intended use case is with mpc to control my music, I just needed to generate a path to the specific folder. – Anjan Sri Sep 08 '19 at 22:19
  • @AnjanSri, Try tree -dlfx ~/Music/ | sed 's/^[^/]*//', (or replace "~/Music/" with the full path of the base dir to be searched). – agc Sep 09 '19 at 00:13

1 Answers1

3

There are two issues here. The first is quoting. You should get into the habit of always quoting your variables. For more on this please see:

However, that's not what's actually breaking your script here. The next problem comes from the fact that you're trying to parse ls. For why that's a bad idea, please see:

Specifically, the problem here is your use of -b which causes a directory named dir one to be displayed as dir\ one. If you pick that from your menu picker, then you will run subdir_check with dir\ one as an argument. Because that's quoted (var is quoted in the function), you are attempting to run ls on the literal dir\ one, the escaped space is not read as being escaped since you're quoting. However, if you don't quote it, it will be read as two separate arguments. So the whole thing is a bit of a mess.

Here's a working version of your script using your basic approach with minor tweaks:

#! /bin/bash
function subdir_check(){
  ## globs that don't match anything should expand to the empty string
  ## instead of expanding to the glob itself as happens by default
  shopt -s nullglob
  ## save all sub directories in the 'dirs' array
  local dirs=("$@"/*/);
  ## Print each dir on its own line and remove the trailing '/'.
  printf '%s\n' "${dirs[@]}" | sed 's|/$||'
}
## Remove the trailing '/' from the input.
directory=$(sed 's|/$||'<<<"$1")
## Quote all the things!
if [[ $(subdir_check "$directory") != "" ]]
then 
    ## You don't need a second variable. Just one: directory. This will now
    ## include the path.
    directory=$(subdir_check "$directory" | rofi -dmenu -p 'Select Subdirectory')
    while [[ $(subdir_check "$directory") != "" ]]
    do
        directory=$(subdir_check "$directory" | rofi -dmenu -p 'Select Subdirectory')
    done
fi
echo "$directory"
terdon
  • 242,166