Create a new folder named the next count - bash

I have a folder of projects, which labels projects based on a number. It starts at 001 and continues counting. I have a bash script I run through Alfred, however, I currently have to type the name of the folder.
QUERY={query}
mkdir /Users/admin/Documents/projects/"$QUERY"
I would like to have the script automatically name the folder to the next number.
For example, if the newest folder is "019" then I would like it to automatically name it to "020"
This is what I've whipped up so far:
nextNum = $(find ~/documents/projects/* -maxdepth 1 -type d -print| wc -l)
numP = nextNum + 1
mkdir /Users/admin/Documents/projects/00"$numP"
I'm not sure if my variable syntax is correct, or if variables are the best way to do this. I am a complete noob to bash so any help is appreciated.

This might be what you're looking for:
#!/bin/bash
cd /Users/admin/Documents/projects || exit
if ! [[ -d 000 ]]; then mkdir 000; exit; fi
dirs=([0-9][0-9][0-9])
mkdir $(printf %03d $(( 1 + 10#${dirs[${#dirs[*]} - 1]} )) )

See comments in code to understand the code.
#!/bin/bash
# Configure your project dir
projects_dir=/Users/admin/Documents/projects
# Find the last project number.
# You can use wc instead of the sort|tail|sed I used. The result is the same.
last_project=$(\ls $projects_dir | sort -n | tail -n1 | sed 's/^0*//')
# Use printf to add the leading '0'
# ${last_project:-0} will substitute '0' when $last_project not set,
# therefore it will work even if your project directory is empty.
# $(( expr )) evaluates math expression. Also you can not have
# subshell expression in $(( expr )) so you can't substitute
# $last_project with the expression above
mkdir $projects_dir/$(printf "%03d" $((${last_project:-0} + 1)))
Although unlikely, the issue with the above script is that hitting a project count over 999 will break the 3 digits directory name convention. A simple fix is to use a 4 digits convention. A directory large amount of files/subdirectories is not recommended anyway so if you do reach 9999 projects, it's best to continue in a second project directory.

Give a try to this funny one:
projects_dir="/Users/admin/Documents/projects/"
[[ -n "${HOME}" ]] && projects_dir="${projects_dir/#~/$HOME}"
mkdir "${projects_dir}"/$(printf "%03d" $(find "${projects_dir}" -maxdepth 1 -type d -printf . | wc -c))
(EDIT)
Expanded version with bash trace output:
set -x
projects_dir="/Users/admin/Documents/projects/"
[[ -n "${HOME}" ]] && projects_dir="${projects_dir/#~/$HOME}"
cd "${projects_dir}" || exit 1
printf "each dot is a directory: "
find "${projects_dir}" -maxdepth 1 -type d -printf .
printf "\n"
let next_directory_id=$(find "${projects_dir}" -maxdepth 1 -type d -printf . | wc -c)
next_directory_name=$(printf "%03d" "${next_directory_id}")
mkdir "${projects_dir}"/"${next_directory_name}"
set +x

targ_number=$(find ~/documents/projects/ -maxdepth 1 -type d -print| wc -l)
mkdir /Users/admin/Documents/projects/$(printf "%03d" $targ_number)

Related

How to change multiple directories' name by computation on the numbers in the names?

I have some directories. Their names are as follows,
s1_tw
s2_tw
s3_tw
s4_tw
How to change their names by add a fixed integer to the number following "s"? How can I change the directories' name to
s22_tw
s23_tw
s24_tw
s25_tw
by changing s1 to s22 (1 + 21 = 22), s2 to s23, etc? (Here adding 21 is expected)
I tried with
for f in s*_tw
do
for i in `seq 1 1 4`
do
mv -i "${f//s${i}/s(${i}+21)}"
done
done
But I know it is not correct, because I can not perform addition operation in this command. Could you please give some suggestions?
This will rename your directories:
#!/bin/bash
find . -maxdepth 1 -type d -name "s?_tw" -print0 | while IFS= read -r -d '' dir
do
digit=$(echo "$dir" | sed 's#./s\([0-9]\)_tw#\1#')
echo "DIGIT=$digit"
(( newdigit = digit + 21 ))
echo "NEWDIGIT=$newdigit"
mv "$dir" "s${newdigit}_tw"
done
The find -print0 with while and read comes from https://mywiki.wooledge.org/BashFAQ/001.

Usage of for loop and if statement in bash

I am using the following code but the final 'echo $dirname' is giving empty output on console
for folderpath in find /u01/app/SrcFiles/commercial/ngdw/* -name "IQ*";
do
folder_count=ls -d $folderpath/* | wc -l
echo -e "Total Date folder created : $folder_count in $folderpath \n"
if [ $folder_count -ne 0 ];
then
for dirpath in `find $folderpath/* -name "2*" `;
do
dirname=${dirpath##*/}
(( dirname <= 20210106 )) || continue
echo $dirname
done
fi
done
First I would calculate the date it was 3 months ago with the date command:
# with GNU date (for example on Linux)
mindate=$(date -d -3month +%Y%m%d)
# with BSD date (for example on macOS)
mindate=$(date -v -3m +%Y%m%d)
Then I would use a shell arithmetic comparison for determining the directories to remove:
# for dirpath in "$directory"/*
for dirpath in "$directory"/{20220310,20220304,20220210,20220203,20210403,20210405}
do
dirname=${dirpath##*/}
(( dirname <= mindate )) || continue
echo "$dirpath"
# rm -rf "$dirpath"
done
== doesn't do wildcard matching. You should do that in the for statement itself.
There's also no need to put * at the beginning of the wildcard, since the year is at the beginning of the directory name, not in the middle.
for i in "$directory"/202104*; do
if [ -d "$i" ]; then
echo "$i"
rm -rf "$i"
fi
done
The if statement serves two purposes:
If there are no matching directories, the wildcard expands to itself (unless you set the nullglob option), and you'll try to remove this nonexistent directory.
In case there are matching files rather than directories, they're skipped.
Suggesting to find which are the directories that created before 90 days ago or older, with find command.
find . -type d -ctime +90
If you want to find which directories created 90 --> 100 days ago.
find . -type d -ctime -100 -ctime +90
Once you have the correct folders list. Feed it to the rm command.
rm -rf $(find . -type d -ctime +90)

Bash Script to Prepend a Single Random Character to All Files In a Folder

I have an audio sample library with thousands of files. I would like to shuffle/randomize the order of these files. Can someone provide me with a bash script/line that would prepend a single random character to all files in a folder (including files in sub-folders). I do not want to prepend a random character to any of the folder names though.
Example:
Kickdrum73.wav
Kickdrum SUB.wav
Kick808.mp3
Renamed to:
f_Kickdrum73.wav
!_Kickdrum SUB.wav
4_Kick808.mp3
If possible, I would like to be able to run this script more than once, but on subsequent runs, it just changes the randomly prepended character instead of prepending a new one.
Some of my attempts:
find ~/Desktop/test -type f -print0 | xargs -0 -n1 bash -c 'mv "$0" "a${0}"'
find ~/Desktop/test/ -type f -exec mv -v {} $(cat a {}) \;
find ~/Desktop/test/ -type f -exec echo -e "Z\n$(cat !)" > !Hat 15.wav
for file in *; do
mv -v "$file" $RANDOM_"$file"
done
Note: I am running on macOS.
Latest attempt using code from mr. fixit:
find . -type f -maxdepth 999 -not -name ".*" |
cut -c 3- - |
while read F; do
randomCharacter="${F:2:1}"
if [ $randomCharacter == '_' ]; then
new="${F:1}"
else
new="_$F"
fi
fileName="`basename $new`"
newFilename="`jot -r -c $fileName 1 A Z`"
filePath="`dirname $new`"
newFilePath="$filePath$newFilename"
mv -v "$F" "$newFilePath"
done
Here's my first answer, enhanced to do sub-directories.
Put the following in file randomize
if [[ $# != 1 || ! -d "$1" ]]; then
echo "usage: $0 <path>"
else
find $1 -type f -not -name ".*" |
while read F; do
FDIR=`dirname "$F"`
FNAME=`basename "$F"`
char2="${FNAME:1:1}"
if [ $char2 == '_' ]; then
new="${FNAME:1}"
else
new="_$FNAME"
fi
new=`jot -r -w "%c$new" 1 A Z`
echo mv "$F" "${FDIR}/${new}"
done
fi
Set the permissions with chmod a+x randomize.
Then call it with randomize your/path.
It'll echo the commands required to rename everything, so you can examine them to ensure they'll work for you. If they look right, you can remove the echo from the 3rd to last line and rerun the script.
cd ~/Desktop/test, then
find . -type f -maxdepth 1 -not -name ".*" |
cut -c 3- - |
while read F; do
char2="${F:2:1}"
if [ $char2 == '_' ]; then
new="${F:1}"
else
new="_$F"
fi
new=`jot -r -w "%c$new" 1 A Z`
mv "$F" "$new"
done
find . -type f -maxdepth 1 -not -name ".*" will get all the files in the current directory, but not the hidden files (names starting with '.')
cut -c 3- - will strip the first 2 chars from the name. find outputs paths, and the ./ gets in the way of processing prefixes.
while read VAR; do <stuff>; done is a way to deal with one line at a time
char2="${VAR:2:1} sets a variable char2 to the 2nd character of the variable VAR.
if - then - else sets new to the filename, either preceded by _ or with the previous random character stripped off.
jot -r -w "%c$new" 1 A Z tacks random 1 character from A-Z onto the beginning of new
mv old new renames the file
You can also do it all in bash and there are several ways to approach it. The first is simply creating an array of letters containing whatever letters you want to use as a prefix and then generating a random number to use to choose the element of the array, e.g.
#!/bin/bash
letters=({0..9} {A..Z} {a..z}) ## array with [0-9] [A-Z] [a-z]
for i in *; do
num=$(($RANDOM % 63)) ## generate number
## remove echo to actually move file
echo "mv \"$i\" \"${letters[num]}_$i\"" ## move file
done
Example Use/Output
Current the script outputs the changes it would make, you must remove the echo "..." surrounding the mv command and fix the escaped quotes to actually have it apply changes:
$ bash ../randprefix.sh
mv "Kick808.mp3" "4_Kick808.mp3"
mv "Kickdrum SUB.wav" "h_Kickdrum SUB.wav"
mv "Kickdrum73.wav" "l_Kickdrum73.wav"
You can also do it by generating a random number representing the ASCII character between 48 (character '0') through 126 (character '~'), excluding 'backtick'), and then converting the random number to an ASCII character and prefix the filename with it, e.g.
#!/bin/bash
for i in *; do
num=$((($RANDOM % 78) + 48)) ## generate number for '0' - '~'
letter=$(printf "\\$(printf '%03o' "$num")") ## letter from number
while [ "$letter" = '`' ]; do ## exclude '`'
num=$((($RANDOM % 78) + 48)) ## generate number
letter=$(printf "\\$(printf '%03o' "$num")")
done
## remove echo to actually move file
echo "mv \"$i\" \"${letter}_$i\"" ## move file
done
(similar output, all punctuation other than backtick is possible)
In each case you will want to place the script in your path or call it from within the directory you want to move the file in (you split split dirname and basename and join them back together to make the script callable passing the directory to search as an argument -- that is left to you)

How to locate the directory where the sum of the number of lines of regular file is greatest (in bash)

Hi i'm new in Unix and bash and I'd like to ask q. how can i do this
The specified directory is given as arguments. Locate the directory
where the sum of the number of lines of regular file is greatest.
Browse all specific directories and their subdirectories. Amounts
count only for files that are directly in the directory.
I try somethnig but it's not working properly.
while [ $# -ne 0 ];
do case "$1" in
-h) show_help ;;
-*) echo "Error: Wrong arguments" 1>&2 exit 1 ;;
*) directories=("$#") break ;;
esac
shift
done
IFS='
'
amount=0
for direct in "${directories[#]}"; do
for subdirect in `find $direct -type d `; do
temp=`find "$subdirect" -type f -exec cat {} \; | wc -l | tr -s " "`
if [ $amount -lt $temp ]; then
amount=$temp
subdirect2=$subdirect
fi
done
echo Output: "'"$subdirect2$amount"'"
done
the problem is here when i use as arguments this dirc.(just example)
/home/usr/first and there are this direct.
/home/usr/first/tmp/first.txt (50 lines)
/home/usr/first/tmp/second.txt (30 lines)
/home/usr/first/tmp1/one.txt (20 lines)
it will give me on Output /home/usr/first/tmp1 100 and this is wrong it should be /home/usr/first/tmp 80
I'd like to scan all directories and all its subdirectories in depth. Also if multiple directories meets the maximum should list all.
Given your sample files, I'm going to assume you only want to look at the immediate subdirectories, not recurse down several levels:
max=-1
# the trailing slash limits the wildcard to directories only
for dir in */; do
count=0
for file in "$dir"/*; do
[[ -f "$file" ]] && (( count += $(wc -l < "$file") ))
done
if (( count > max )); then
max=$count
maxdir="$dir"
fi
done
echo "files in $maxdir have $max lines"
files in tmp/ have 80 lines
In the spirit of Unix (caugh), here's an absolutely disgusting chain of pipes that I personally hate, but it's a lot of fun to construct :):
find . -mindepth 1 -maxdepth 1 -type d -exec sh -c 'find "$1" -maxdepth 1 -type f -print0 | wc -l --files0-from=- | tail -1 | { read a _ && echo "$a $1"; }' _ {} \; | sort -nr | head -1
Of course, don't use this unless you're mentally ill, use glenn jackman's nice answer instead.
You can have great control on find's unlimited filtering possibilities, too. Yay. But use glenn's answer!

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