Execute command in all immediate subdirectories - bash

I'm trying to add a shell function (zsh) mexec to execute the same command in all immediate subdirectories e.g. with the following structure
~
-- folder1
-- folder2
mexec pwd would show for example
/home/me/folder1
/home/me/folder2
I'm using find to pull the immediate subdirectories. The problem is getting the passed in command to execute. Here's my first function defintion:
mexec() {
find . -mindepth 1 -maxdepth 1 -type d | xargs -I'{}' \
/bin/zsh -c "cd {} && $#;";
}
only executes the command itself but doesn't pass in the arguments i.e. mexec ls -al behaves exactly like ls
Changing the second line to /bin/zsh -c "(cd {} && $#);", mexec works for just mexec ls but shows this error for mexec ls -al:
zsh:1: parse error near `ls'
Going the exec route with find
find . -mindepth 1 -maxdepth 1 -type d -exec /bin/zsh -c "(cd {} && $#)" \;
Gives me the same thing which leads me to believe there's a problem with how I'm passing the arguments to zsh. This also seems to be a problem if I use bash: the error shown is:
-a);: -c: line 1: syntax error: unexpected end of file
What would be a good way to achieve this?

Can you try using this simple loop which loops in all sub-directories at one level deep and execute commands on it,
for d in ./*/ ; do (cd "$d" && ls -al); done
(cmd1 && cmd2) opens a sub-shell to run the commands. Since it is a child shell, the parent shell (the shell from which you're running this command) retains its current folder and other environment variables.
Wrap it around in a function in a proper zsh script as
#!/bin/zsh
function runCommand() {
for d in ./*/ ; do /bin/zsh -c "(cd "$d" && "$#")"; done
}
runCommand "ls -al"
should work just fine for you.

#!/bin/zsh
# A simple script with a function...
mexec()
{
export THE_COMMAND=$#
find . -type d -maxdepth 1 -mindepth 1 -print0 | xargs -0 -I{} zsh -c 'cd "{}" && echo "{}" && echo "$('$THE_COMMAND')" && echo -e'
}
mexec ls -al

using https://github.com/sharkdp/fd but you could as well use plain old find instead of fdfind
function inDirs() { fdfind --type d --max-depth 1 --exec bash -c "x={} && echo && echo \$x && echo \${x//?/=} && cd {} && echo '-> '$* && $*" ; }

Related

Bash conditional with shell variable

I want to check if a file exists with [ -f "$1" ] but it's not working. The command is working with plain text like [ -f "filename.xml" ].
I echoed $1 which is for example filename.xml. Any ideas?
sourcePath=/SPECIFICPATH/${1};
echo $sourcePath;
echo $1;
find /EXAMPLEPATH -name pages -type d -execdir bash -c 'cd pages && [ -f "$1" ] && pwd && cp $sourcePath .' \;
I'm working in automator using a shell script block.
You’re invoking an entirely new shell with bash -c …, so you need to pass $1 along. Same with $sourcePath, if it’s not exported.
find /EXAMPLEPATH -name pages -type d -execdir bash -c 'cd pages && [ -f "$1" ] && pwd && cp "$2" .' bash "$1" "$sourcePath" \;
(In bash -c … bash "$1" "$sourcePath", the second bash is $0.)
There's no need for the subshell if you move some of the logic to the find command.
find /EXAMPLEPATH -wholename "*/pages/$1" -print -execdir cp "$sourcePath" . \;
-wholename matches a file named $1 in a pages directory.
-print replaces pwd.
Now you can call cp directly.

bash loop in parallel

I am trying to run this script in parallel, for i<=4 in each set. The runspr.py is itself parallel, and thats fine. What I am trying to do is running only 4 i loop in any instance.
In my present code, it will run everything.
#!bin/bash
for i in *
do
if [[ -d $i ]]; then
echo "$i id dir"
cd $i
python3 ~/bin/runspr.py SCF &
cd ..
else
echo "$i nont dir"
fi
done
I have followed https://www.biostars.org/p/63816/ and https://unix.stackexchange.com/questions/35416/four-tasks-in-parallel-how-do-i-do-that
but unable to impliment the code in parallel.
You don't need to use for loop. You can use gnu parallel like this with find:
find . -mindepth 1 -maxdepth 1 -type d ! -print0 |
parallel -0 --jobs 4 'cd {}; python3 ~/bin/runspr.py SCF'
Another possible solution is:
find . -mindepth 1 -maxdepth 1 -type d ! -print0 |
xargs -I {} -P 4 sh -c 'cd {}; python3 ~/bin/runspr.py SCF'

gbash 'git rm' multiple files that are found by a 'find' command

I want to 'git rm' a bunch of files that are found by a 'find' command. The files should have a certain suffix. I got this:
TEST_PATH='/usr/src'
function main() {
for i in "$#"
do
echo "current i = ${i}"
COMMAND='find $TEST_PATH -maxdepth 20 -name '*_${i}.txt' -exec git rm {} \;'
# COMMAND="$(find $TEST_PATH -maxdepth 20 name '*_${i}.txt' -print0 | xargs -0 -I{} cp {} .)"
# COMMAND="find $TEST_PATH -maxdepth 20 -name '*_${i}.txt' -exec cp {} . \;"
# COMMAND="find . '*.BUILD' | while read file; do echo "$file"; done \;"
done
echo "Running Command: $COMMAND"
$COMMAND
}
gbash::main "$#"
Running it will throw an error like this:
$ sh abc.sh 123
current i = 123
Running Command: find ../../src/python/servers/innertube/tests/ -maxdepth 20 -name "*_9421870.txt" -exec rm {}\;
find: missing argument to `-exec'
I've read and tried all the solutions on stackoverflow (see the commented out code) but none works...
Update
The problem is that you should eval contents of the variable containing command:
eval $COMMAND
From man eval:
The eval utility shall construct a command by concatenating arguments together, separating each with a <space> character. The constructed command shall be read and executed by the shell.
Original answer
Replace {}\; with {} \; or {} +.
Read the man page for find. The action used in your command is documented as:
-exec command ;
Execute command; true if 0 status is returned. All following arguments > to find are taken to be arguments to the command until an argument consisting of ; is encountered. The string {} is replaced by the current file name being processed everywhere it occurs in the arguments to the command...
So the command failed because the {}\; sequence is interpreted as command.

How to use a function for a simultaneously run process in bash?

#!/bin/bash
function func_name {
do something
}
find . -name "*" -type d -exec bash -c '( cd {} &&
func_name;
)' bash $1 $2 {} \;
$1 and $2 are commandline arguments unrelated to the question asked I believe. I am trying to go to all sub-directories and run a function,
but I get a message "func_name: command not found"
In your code, func_name is invoked within a sub-shell with a totally new environment, thus you should export it first:
#!/bin/bash
function func_name {
do something
}
export -f func_name
find . -name "*" -type d -exec bash -c '( cd {} &&
func_name;
)' bash $1 $2 {} \;

Create an if statement in Bash commandline (not a script)

I am trying to create an if statement in bash command line, not a script but a statement can be typed into the command line with no returns because I have to run this script through a groovy command line call.
if [cat $(find ./ -name userId.txt) == "517980"]; then cat $(find ./ -name userId.txt); fi
The Jenkins groovy script looks like this
node("puppet-$ENVIRONMENT") {
sh "/opt/puppet/bin/puppet module uninstall ${module} || echo 'NOT INSTALLED!'"
sh "pwd"
sh "rm -rf *"
//unarchive the tar in the remote file system and install it
unarchive mapping: ['*.*': './']
sh 'if [cat $(find ./ -name userId.txt) == "517980"]; then echo "it works"; fi'
sh "ls -alrt"
sh '/opt/puppet/bin/puppet module install --force $(find ./ -name *.tar.gz)'
}
file=$(find ./ -name userId.txt) && [ -n "$file" ] && { contents=$(cat "$file"); [ "$contents" == "517980" ] && echo "$contents"; }
don't need to run find or cat more than once
[ is actually a command not mere syntax: it needs a space to separate it from its arguments
You're missing $() around your cat calls

Resources