0

I'm trying to list all files in a sub-directory without their path, I just want the file name and extension, but Bash substitution doesn't work with all paths in an array, it only works with the first element.

Code:

#!/usr/bin/bash

NAME="$(pwd | grep -o '[^/]*$')"

# ls src -R
PATH="$(ls src/*.{cpp,hpp} 2> /dev/null)"
if [ 0 -eq "${#PATH[@]}" ]; then
    echo "The project has no source code file yet."
    exit 0
fi

EMPTY=''
for FILE in "${PATH[@]}"; do
    echo "${FILE/src\//$EMPTY}"
done

Directory tree:

FileReader
├── bin
├── make.sh
├── obj
└── src
    ├── FileReader.cpp
    ├── FileReader.hpp
    └── main.cpp

Expected:

$ bash make.sh

FileReader.cpp
FileReader.hpp
main.cpp

Output:

$ bash make.sh

FileReader.cpp
src/FileReader.hpp
src/main.cpp
6
  • Could you use this - ( cd src ; ls *.{cpp,hpp} ) Commented Mar 26, 2021 at 14:23
  • Or find -printf "%f\n" Commented Mar 26, 2021 at 14:24
  • Paste your code at shellcheck.net Commented Mar 26, 2021 at 14:24
  • Have a look at dirname , also avoid using upper case variables in bash to avoid varname collision, PATH is already taken. Also PATH in your code is not an array. Commented Mar 26, 2021 at 14:28
  • @MrR thanks for your solution, it worked! Commented Mar 26, 2021 at 15:00

1 Answer 1

1

Since parsing ls is bad, I'd do something like:

#!/usr/bin/env bash

# If no matching files, globs expand to an empty string instead of the pattern
shopt -s nullglob
declare -i count=0
for file in src/*.[ch]pp; do
    count+=1
    printf "%s\n" "$(basename "$file")"
done

[[ $count -eq 0 ]] && echo "The project has no source code file yet."

to avoid issue with funny characters in filenames. basename(1) removes leading directory components from a filename (And optionally a given extension).

You can also safely get the files in an array with files=( src/*.[ch]pp ) and use something closer to your original approach. I would definitely avoid calling a variable PATH though as that conflicts with a built in one, though.

Array based version (This one uses the ${variable#pattern} parameter expansion syntax that strips the matched text of a pattern from the beginning of the variable's value):

#!/usr/bin/env bash

shopt -s nullglob
files=( src/*.[ch]pp )

if [[ "${#files[@]}" -eq 0 ]]; then
    echo "The project has no source code file yet."
else
    printf "%s\n" "${files[@]#*/}"
fi
Sign up to request clarification or add additional context in comments.

2 Comments

I kind of like the second version better, actually, because it removes the explicit for loop.
and removes all the sub-processes to do basename so if there were lots of files should be a heap faster.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.