Find folder position number in folder listing - bash

Suppose i have a /home folder with these sub-folders:
/home/alex
/home/luigi
/home/marta
I can list and count all sub-folders in a folder like this:
ls 2>/dev/null -Ubad1 -- /home/* | wc -l
3
But I need to find the position (2, in this example) if folder (or basename) is === luigi
Is this possible in bash?

After William Pursell comment, this does the job:
ls 2>/dev/null -Ubad1 -- /home/* | awk '/luigi$/{print NR}'
2
Note the $ at the end, this will avoid doubles like joe and joey.
Thanks.

You could just use a bash shell loop over the wildcard expansion, keeping an index as you go, and report the index when the base directory name matches:
index=1
for dir in /home/*
do
if [[ "$dir" =~ /luigi$ ]]
then
echo $index
break
fi
((index++))
done
This reports the position (among the expansion of /home/*) of the "luigi" directory -- anchored with the directory separator / and the end of the line $.

$ find /home/ -mindepth 1 -maxdepth 1 -type d | awk -F/ '$NF=="luigi" {print NR}'
2
$ find /home/ -mindepth 1 -maxdepth 1 -type d | awk -F/ '$NF=="alex" {print NR}'
1

Better use find to get subfolders and a list to iterate with indexes:
subfolders=($(find /home -mindepth 1 -maxdepth 1 -type d))
Find will get realpath, so if you need relative you can use something like:
luigi_folder=${subfolders[2]##*/}

Related

Get files from directories alphabetically sorted with bash

I have this code that works in the directory that I execute:
pathtrabajo=.
filedirs="files.txt"
dirs=$(find . -maxdepth 1 -mindepth 1 -type d | sort -n)
SAVEIFS=$IFS
IFS=$(echo -en "\n\b")
for entry in $dirs
do
echo "${entry}" >> "${filedirs}"
find "$entry" -maxdepth 1 -mindepth 1 -name '*.md' -printf '%f\n' | sort | sed 's/\.md$//1' | awk '{print "- [["$0"]]"}' >> "${filedirs}"
done
IFS=$SAVEIFS
But when I try to make it global to work with variables, find gives error:
pathtrabajo="/path/to/a/files"
filedirs="files.txt"
dirs=$(find "${pathtrabajo}" -maxdepth 1 -mindepth 1 -type d | sort -n)
SAVEIFS=$IFS
IFS=$(echo -en "\n\b")
for entry in "${dirs[#]}"
do
echo "${entry}" >> "${pathtrabajo}"/"${filedirs}"
find "${entry}" -maxdepth 1 -mindepth 1 -name '*.md' -printf '%f\n' | sort | sed 's/\.md$//1' | awk '{print "- [["$0"]]"}' >> "${pathtrabajo}"/"${filedirs}"
done
IFS=$SAVEIFS
What did I do wrong?
It's really not clear why you are using find here at all. The following will probably do what you are trying if I can guess from your code.
dirs=([0-9][!0-9]*/ [0-9][0-9][!0-9]*/ [0-9][0-9][0-9][!0-9]*/ [!0-9]*/)
printf "%s\n" "${dirs[#]}" >"$filedirs"
for dir in "${dirs[#]}"; do
printf "%s\n" "$dir"/*.md |
awk '{ sub(/\.md$/, ""); print "- [["$0"]]" }'
done >>"$filedirs"
The shell already expands wildcards alphabetically. The dirs assignment will expand all directories which start with a single digit, then the ones with two digits, then the ones with three digits -- extend if you need more digits -- then the ones which do not start with a digit.
It would not be hard, but cumbersome, to extend the code to run in an arbitrary directory. My proposed solution would be (for once!) to cd to the directory where you want the listing, then run this script.

How to get list of certain strings in a list of files using bash?

The title is maybe not really descriptive, but I couldn't find a more concise way to describe the problem.
I have a directory containing different files which have a name that e.g. looks like this:
{some text}2019Q2{some text}.pdf
So the filenames have somewhere in the name a year followed by a capital Q and then another number. The other text can be anything, but it won't contain anything matching the format year-Q-number. There will also be no numbers directly before or after this format.
I can work something out to get this from one filename, but I actually need a 'list' so I can do a for-loop over this in bash.
So, if my directory contains the files:
costumerA_2019Q2_something.pdf
costumerB_2019Q2_something.pdf
costumerA_2019Q3_something.pdf
costumerB_2019Q3_something.pdf
costumerC_2019Q3_something.pdf
costumerA_2020Q1_something.pdf
costumerD2020Q2something.pdf
I want a for loop that goes over 2019Q2, 2019Q3, 2020Q1, and 2020Q2.
EDIT:
This is what I have so far. It is able to extract the substrings, but it still has doubles. Since I'm already in the loop and I don't see how I can remove the doubles.
find original/*.pdf -type f -print0 | while IFS= read -r -d '' line; do
echo $line | grep -oP '[0-9]{4}Q[0-9]'
done
# list all _filanames_ that end with .pdf from the folder original
find original -maxdepth 1 -name '*.pdf' -type f -print "%p\n" |
# extract the pattern
sed 's/.*\([0-9]{4}Q[0-9]\).*/\1/' |
# iterate
while IFS= read -r file; do
echo "$file"
done
I used -print %p to print just the filename, instead of full path. The GNU sed has -z option that you can use with -print0 (or -print "%p\0").
With how you have wanted to do this, if your files have no newline in the name, there is no need to loop over list in bash (as a rule of a thumb, try to avoid while read line, it's very slow):
find original -maxdepth 1 -name '*.pdf' -type f | grep -oP '[0-9]{4}Q[0-9]'
or with a zero seprated stream:
find original -maxdepth 1 -name '*.pdf' -type f -print0 |
grep -zoP '[0-9]{4}Q[0-9]' | tr '\0' '\n'
If you want to remove duplicate elements from the list, pipe it to sort -u.
Try this, in bash:
~ > $ ls
costumerA_2019Q2_something.pdf costumerB_2019Q2_something.pdf
costumerA_2019Q3_something.pdf other.pdf
costumerA_2020Q1_something.pdf someother.file.txt
~ > $ for x in `(ls)`; do [[ ${x} =~ [0-9]Q[1-4] ]] && echo $x; done;
costumerA_2019Q2_something.pdf
costumerA_2019Q3_something.pdf
costumerA_2020Q1_something.pdf
costumerB_2019Q2_something.pdf
~ > $ (for x in *; do [[ ${x} =~ ([0-9]{4}Q[1-4]).+pdf ]] && echo ${BASH_REMATCH[1]}; done;) | sort -u
2019Q2
2019Q3
2020Q1

count of files only in a list

I have a txt file that has the list of files an folders per each line. I would like to have the only count of files not folders.
Count=$(cat list.txt | wc -l)
will give me the total count.
Test the type of each file in a loop:
count=0
IFS=$'\n' # so spaces in filenames don't cause a problem
for i in $(< list.txt); do
if [ -f "$i" ]
then count=$((count + 1))
fi
done
below is very straight forward
find . -maxdepth 1 -type d -printf '%f\n' | wc -l

How to use find utility with logical operators and post processing

Is there way to find all directories that have executable file that matches a partial name of parent directory?
Situation
/distribution/software_a_v1.0.0/software_a
/distribution/software_a_v1.0.1/software_a
/distribution/software_a_v1.0.2/config.cfg
I need result
/distribution/software_a_v1.0.0/software_a
/distribution/software_a_v1.0.1/software_a
I've gotten only so far
find /distribution -maxdepth 1 -type d #and at depth 2 -type f -perm /u=x and binary name matches directory name, minus version
Another way using awk:
find /path -type f -perm -u=x -print | awk -F/ '{ rec=$0; sub(/_v[0-9].*$/,"",$(NF-1)); if( $NF == $(NF-1) ) print rec }'
The awk part is based on your sample and stated condition ... name matches directory name, minus version. Modify it if needed.
I would use grep:
find /distribution -maxdepth 1 -type d | grep "/distribution/software_\w_v\d*?\.\d*?\.\d*?/software_\w"
I don't know if this is the most efficient, but here's one way you could do it, using just bash...
for f in /distribution/*/*
do
if [[ -f "${f}" && -x "${f}" ]] # it's a file and executable
then
b="${f##*/} # get just the filename
[[ "${f}" =~ "/distribution/${b}*/${b}" ]] && echo "${f}"
fi
done

Recursively count specific files BASH

My goal is to write a script to recursively search through the current working directory and the sub dirctories and print out a count of the number of ordinary files, a count of the directories, count of block special files, count of character special files,count of FIFOs, and a count of symbolic links. I have to use condition tests with [[ ]]. Problem is I am not quite sure how to even start.
I tried the something like the following to search for all ordinary files but I'm not sure how recursion exactly works in BASH scripting:
function searchFiles(){
if [[ -f /* ]]; then
return 1
fi
}
searchFiles
echo "Number of ordinary files $?"
but I get 0 as a result. Anyone help on this?
Why would you not use find?
$ # Files
$ find . -type f | wc -l
327
$ # Directories
$ find . -type d | wc -l
64
$ # Block special
$ find . -type b | wc -l
0
$ # Character special
$ find . -type c | wc -l
0
$ # named pipe
$ find . -type p | wc -l
0
$ # symlink
$ find . -type l | wc -l
0
Something to get you started:
#!/bin/bash
directory=0
file=0
total=0
for a in *
do
if test -d $a; then
directory=$(($directory+1))
else
file=$(($file+1))
fi
total=$(($total+1))
echo $a
done
echo Total directories: $directory
echo Total files: $file
echo Total: $total
No recursion here though, for that you could resort to ls -lR or similar; but then again if you are to use an external program you should resort to using find, that's what it's designed to do.

Resources