How to strip paths from file names on standard input [duplicate] - bash

This question already has answers here:
Extract filename and extension in Bash
(38 answers)
Read file line by line and perform action for each in bash
(3 answers)
Closed 3 years ago.
How could I strip the paths from a list of file names on standard input? I basically need something like basename that iterates over standard input.

while read path; do basename "$path" ; done
Simple loop.

Try xargs basename -a. It'll run faster than a simple loop as basename won't be run for each filename.
$ printf "%s\n" foo/bar bar/baz | xargs basename -a
bar
baz
Note that the above won't correctly handle filenames with spaces. You'll want to use the -0 option to xargs and ensure the input is NUL terminated.
$ printf "%s\0" 'foo/foo bar' bar/baz | xargs -0 basename -a
foo bar
baz

According to this answer
If your filename is in the form of a variable such as $fullpathname
nopathfile="${fullpathname##*/}"
It's in the bash manpage at the section called "Parameter Expansion"

Related

How do I pipe into printf? [duplicate]

This question already has answers here:
Piping not working with echo command [duplicate]
(4 answers)
How to pass command output as multiple arguments to another command
(5 answers)
Closed 1 year ago.
I'm going nuts trying to understand what is the problem with this simple example (zsh or bash):
echo -n "6842" | printf "%'d"
The output is 0... why though? I'd like the output to be 6,842
Thanks in advance, I've had no luck for an hour now using google trying to figure this out...!
printf doesn't read arguments to format from standard input, but from the command line directly. For example, this works:
$ printf "%'d" 6842
6,842
You can convert output of a command to command-line arguments using command substitution:
$ printf "%'d" $(echo -n 6842)
6,842
If you want to invoke printf inside a pipeline, you can use xargs to read input and execute printf with the appropriate arguments:
echo -n "6842" | xargs printf "%'d"
printf does not format data passed to it on standard input; it takes a set of arguments, the first of which is the format, and the remainder are the values to display.
Luckily, this is exactly what xargs is for; to quote the manual:
xargs - build and execute command lines from standard input
So instead of piping to printf directly, you can pipe to xargs, and tell it to run printf for you with the given arguments. In short:
echo -n "6842" | xargs printf "%'d"

How can I create a filename using bash command parameter? [duplicate]

This question already has answers here:
How to use > in an xargs command?
(4 answers)
Closed 2 years ago.
I'm trying to create a filename using command param as such but not sure how to go about doing it. This is what I was trying:
echo "AZ" |xargs date >> $1.txt
I'm trying to create a file named AZ.txt with the date in it.
If you must use xargs, then you'll have to wrap the rest of it in a shell script to delay execution of the redirection until you have constructed the filename:
echo AZ | xargs sh -c 'date >> "$1".txt' sh
The trailing "sh" is to force the xargs argument "AZ" into $1 instead of $0
If the name of the file comes from some command, you can use command substitution:
date > "$(echo AZ)".txt

Correct way to assign regex replace to a variable in bash [duplicate]

This question already has answers here:
Send string to stdin
(6 answers)
Command not found error in Bash variable assignment
(5 answers)
How do I set a variable to the output of a command in Bash?
(15 answers)
Closed 3 years ago.
I am trying to assign a variable to the result of a regex substitution in bash. For instance, when I run
echo $initialvar | perl -pe 's/.+(Some_Dir\/)(.+)/\2/'
I get the desired output from the echo. How would I assign a newvar to the resulting output?
I've tried:
newvar= "$($initialvar | perl -pe 's/.+(Some_Dir\/)(.+)/\2/')"
newvar= echo $initialvar | perl -pe 's/.+(Some_Dir\/)(.+)/\2/'
but none of them work
As for your question, it seems to consist of two challenges: One being assigning the output of running a command to a BASH variable, and the other being piping the content of a variable to the standard input of a Perl program.
One
foo=$(bar)
runs bar and saves its output to the BASH variable $foo.
The other
Any of
echo "$foo" | bar
bar <(echo "$foo")
bar <<< "$foo"
runs bar with the content of $foo piped to its standard input
Combining the two
baz=$(bar <<< "$foo")
sets $baz to the value produced by bar having the content of $foo sent to its standard input.
Secondly, a few Perl-related suggestions peripherally related to your question:
I might instead use perl -nE:
-n will loop through each line like -p, but won't print by default.
-E will evaluate like -e, but with experimental features like say enabled.
You can avoid escaping the slash in Some_Dir/ by using another separator, e.g. s!...!...! or m!...!. Instead of replacing with s//, since you're just printing "Some_Dir/" if it matches, you might as well go and do that directly:
perl -nE 'say (m!Some_Dir/! ? "Some_Dir/" : $_)'
So:
newvar=$(perl -nE 'say (m!Some_Dir/! ? "Some_Dir/" : $_)' <<< "$initialvar")
You can use either of the following:
newvar=$(echo "$initialvar" | perl -pe 's/.+(Some_Dir\/)(.+)/\2/')
newvar=`echo "$initialvar" | perl -pe 's/.+(Some_Dir\/)(.+)/\2/'`

BASH script mv command would not work without double-quoting variables. Why? [duplicate]

This question already has answers here:
When to wrap quotes around a shell variable?
(5 answers)
Closed 7 years ago.
I wrote a script today as follows:
echo "Enter a directory path"
read dir
for file in $dir/[!.]*;
do
f=`echo $file | sed 's/ /_/g'`
mv "${file}" "${f}"
done
Initially, the mv command was written as:
mv ${file} ${f}
But that line was throwing
usage: mv [-f | -i | -n] [-v] source target
mv [-f | -i | -n] [-v] source ... directory
I was able to use google to figure out that the variables needed to be wrapped in double quotes, but I still don't understand why doing so resolved the problem?
Thanks!
Quoting, in shell, prevents string-splitting and glob expansion. If you don't double-quote your variables, you have no idea how many arguments each variable may expand into after these parsing steps are run.
That is:
mv $foo $bar
...may turn into...
mv ./first word second word third word destination-file
if
foo='./first word second word third word'
bar='destination-file'
Similarly, consider the case where you have a filename containing a glob expression:
foo='hello * world'
In that case, your mv command would get a list of all files in the current directory.
...or, consider the case when an argument is empty:
foo='hello world'
bar=''
In this case, instead of (properly) getting an error that you can't have file with a 0-byte name, you would be trying to rename a file named hello to world: The $bar simply disappears.

How can I write an Automator action in bash with files and folders as arguments?

When I create a Automator action in XCode using Bash, all files and folder paths are printed to stdin.
How do I get the content of those files?
Whatever I try, I only get the filename in the output.
If I just select "Run shell script" I can select if I want everything to stdin or as arguments. Can this be done for an XCode project to?
It's almost easier to use Applescript, and let that run the Bash.
I tried something like
xargs | cat | MyCommand
What's the pipe between xargs and cat doing there? Try
xargs cat | MyCommand
or, better,
xargs -R -1 -I file cat "file" | MyCommand
to properly handle file names with spaces etc.
If, instead, you want MyComand invoked on each file,
local IFS="\n"
while read filename; do
MyCommand < $filename
done
may also be useful.
read will read lines from the script's stdin; just make sure to set $IFS to something that won't interfere if the pathnames are sent without backslashes escaping any spaces:
OLDIFS="$IFS"
IFS=$'\n'
while read filename ; do
echo "*** $filename:"
cat -n "$filename"
done
IFS="$OLDIFS"

Resources