Search code examples
androidshadbglobls

Extra ":" at the end of output from sudo su -c ls, only when globbing is used


Using adb shell to run commands on an android device, I get different results when running ls with or without a wildcard ( globbing, i.e * ).

When running ls without a wildcard, the last path is displayed properly. When running ls with a wildcard, the path is displayed with an : in the end of it for some reason. The actual file does not have a : in its path.

My issue is specifically with the last file: /data/data/com.kauf.wrapmyFaceFunphotoeditor/files/DV-com.com.kauf.wrapmyFaceFunphotoeditor-2020-05-17-17-44-30-DEBUG.txt:

it has an : in the end which isn't supposed to be there

Why does using a wildcard in ls add characters to the result path?

Edit, environment details: Windows 10 / Android 7, the code is running on sh. I've ran adb shell to get to this command prompt, and doing it in one line (i.e adb shell su -c ls ...) returns similar results, same for adb shell command ...; also clarified the question.


Solution

  • As described in Why you shouldn't parse the output of ls, ls's behavior is not always well-defined. It's generally safer to use NULs (if you don't have any control or knowledge of filenames) or newlines (if you have reason to be certain that filenames can't contain them) to directly delimit a list of values emitted by the shell. Consider, then:

    # output is separated by NULs, which cannot possibly exist in filenames
    printf '%s\0' /data/data/com.kauf.wrapmyfacefunphotoeditor/files/DV-*
    

    ...or...

    # output is separated by newlines; beware of a file named DV-evil<newline>something-else
    printf '%s\n' /data/data/com.kauf.wrapmyfacefunphotoeditor/files/DV-*
    

    Note that if you're passing this through extra unescaping layers, it may be necessary to double up your backslashes -- if you see literal 0s or ns separating filenames in your output, that's evidence of same.


    Note also that if no matching files exist, a glob will expand to itself, so you can get an output that contains only the literal string /data/data/com.kauf.wrapmyfacefunphotoeditor/files/DV-*; in bash this can be suppressed with shopt -s nullglob, but with /bin/sh (particularly the minimal busybox versions more likely to be available on Android) this may not be available. One way to work around this is with code similar to the following:

    # set list of files into $1, $2, etc
    set -- /data/data/com.kauf.wrapmyfacefunphotoeditor/files/DV-*
    # exit immediately if $1 does not exist
    if [ "$#" -le 1 ] && [ ! -e "$1" ]; then
      exit
    fi
    # otherwise, print the list in our desired format
    printf '%s\0' "$@"