Search code examples
stringwindowsbashfunctionls

How to format a list of strings into a set of command option/argument pairs in a Bash function


What I want to do

I'm running Git Bash on Windows, and I'm attempting to write a wrapper function for ls which handles Windows' hidden file flags correctly.

The command cmd.exe /c "dir /b /a/h" will output a list of files in a directory which have a hidden flag. Each filename is output on one line, separated by \r\n (being Windows). Filenames with spaces are surrounded with single quotes.

$ cmd.exe /c "/b /ah"
.gitconfig
desktop.ini
'hidden file.txt' 

I then want to format this as a list of --ignore options for the final ls call.

--ignore='.gitconfig' --ignore='desktop.ini' --ignore='hidden file.txt'

What I've tried

I'm using a for loop, after setting IFS='\r\n', which should allow me to format each filename string with --ignore=' and '.

function ls {
    options=""
    IFS="\r\n"
    for filename in $(cmd.exe /c "dir /b /ah")
        do options="${options}--ignore='$filename' "
    done

    echo $options
    # command ls $options "$@"
}

However, the string isn't being split, and all instances of n and r in the output are being replaced with a space, so the resulting string is garbled.

$ ls
--ig o e='.gitco fig
desktop.i i
hidde  file.txt'

What am I doing wrong?


Solution

  • Collecting options into a single string variable is going to end up badly. Use an array instead.

    ls () {
        options=()
        local IFS="\r\n"
        while read -r filename; do
            case $filename in \'*\')
                filename=${filename#\'}; filename=${filename%\'};;
            esac
            options+=("--ignore=$filename")
        done < <(cmd.exe /c "dir /b /ah")
    
        # echo will flatten the arguments, maybe just run git here
        echo "${options[@]}"
    }
    

    See also http://mywiki.wooledge.org/BashFAQ/050 for an extended discussion.