How to use a function for a simultaneously run process in bash? - 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 {} \;

Related

Exec not outputting result to function

How do I get the result of the find to be used by the function? I keep getting blank results.
#!/bin/bash
#functions
function codec_scan () {
echo "paremter 1 is this: $1" # >> $fulllog
}
#exporting
export -f codec_scan
#Main Code
find . -type f \( -name "*.avi" -o -name "*.AVI" -o -name "*.m4v" -o -name "*.mkv" -o -name "*.mp4" -o -name "*.MP4" \) -exec bash -c codec_scan \"\{}\" \;
\"\{}\" is literal characters " with {} inside. It's a sole {}. Nothing before it, nothing after it. Unless you want to add a literal " characters.
It's bash -c 'script...'. The arguments that follow are arguments to the script, not to the function. Each function has its own $1 $2 ... positional arguments, they are separate to script. And they arguments to bash -c start from $0, not from $1, see man bash, it is like bash -c 'script..' <$0> <$1> <$2> ...
You want:
find .... -exec bash -c 'codec_scan "$#"' _ {} \;
Do not use function in bash. Just codec_scan() {. See https://wiki.bash-hackers.org/scripting/obsolete
You may be interested in -iname.

Trap output in exec part of find

myfunc() {
echo "Hello"
}
export -f myfunc
find / -type f -exec bash -c 'myfunc "$0"' {} \;
var="$(myfunc)"
echo "$var"
This will return Hello, I know. But is there a way I can trap $var in exec part
of find?
I would like to append result of find to global array.
If you want the result of find, you have to put the find command between the $( ).
If you want an array, you have to use extra ( ).
var=($(find / -type f -exec bash -c 'myfunc "$0"' {} \;))

bash rename file/add integer in filename with various extensions in multiple subdirectories

I want to insert an integer into filenames with various extensions in multiple subdirectories using bash.
Examples:
./trial2/foo.hhh --> ./trial2/foo1.hhh
./trial2/trial3/foo.txt--> ./trial2/trial3/foo1.txt
I tried to separate the filename from the extension and insert the integer in between with:
i=123
find . -type f -exec sh -c ' echo mv "$0" "${0%.*}${i}.${0##*.}" ' {} \;
mv ./trial2/foo.hhh ./trial2/foo.hhh
But the variable output ${i} is not printed. Why?
If I change to:
find . -type f -exec sh -c ' mv "$0" "${0%.*}123.${0##*.}" ' {} \;
mv ./trial2/foo.hhh ./trial2/foo123.hhh
The number is printed. However, I need the variable ${i} as it will be defined in a wrapping for-loop.
Edit
You are almost there; you just hit a quoting subtlety. i is declared in the current shell, so needs to be passed into the sh run by find somehow. In your current command, ${i} is in single quotes, so is not interpreted by bash before the quoted material is passed to sh.
As suggested by Charles Duffy:
find . -type f -exec sh -c ' echo mv "$0" "${0%.*}${1}.${0##*.}" ' {} "$i" \;
Within the sh command, $0 is the {}, as you know. $1 is the second parameter to the sh, which is "$i" (i expanded as a single word). Instead of ${i}, the sh command uses ${1} to access that parameter (a copy of i).
Original
In this example, i is interpolated in the current shell.
Before: find . -type f -exec sh -c ' echo mv "$0" "${0%.*}${i}.${0##*.}" ' {} \;
After: find . -type f -exec sh -c ' echo mv "$0" "${0%.*}'"${i}"'.${0##*.}" ' {} \;
^^ ^^
The '"${i}"' drops you out of the single quotes, then expands i, then takes you back into the single quotes. That way the command you are passing to sh includes the value of i you want.
Pass $i in $0, and your filenames in $1 and onward, and you can change from -exec ... {} \; to -exec ... {} +, thus using only one sh instance to rename potentially several files.
The following requires bash, but generates mv commands that are guaranteed to be correct even in the presence of malicious or confusing filenames (with spaces, newlines, control characters, etc):
find . -type f -exec bash -c '
logcmd() { printf "%q " "$#"; printf "\n"; } # a more accurate replacement for echo
for f; do
logcmd mv "$f" "${f%.*}${0}.${f##*.}"
done' "$i" {} +

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.

Execute command in all immediate subdirectories

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 '-> '$* && $*" ; }

Resources