This question already has answers here:
Extract filename and extension in Bash
(38 answers)
Closed 3 years ago.
I want to remove the file name extension from a variable based on the extension pattern. Currently, I am interested only to remove two extensions - exe and .cat. I can achieve this using multiple commands. But, wondering if there is a short cut available with a single command.
filename=testfile.exe
file=${testfile%.exe}
This will give me the output as "testfile" but if my filename now is:
filename=testfile.cat
I will have to again run:
file=${testfile%.cat}
I want a single command which will give me "testfile" as output.
I don't want to use file=${testfile%.*} as this will remove all the extensions of all other undesired types.
Thanks in Advance!
Updated to actually answer the question.
You will need to enable the extglob shell option which enables the #(pattern-list). This matches one of the given patterns. In the pattern below the '|' is an or operator and the '$' is a end of string match to avoid
Example Code:
$ shopt -s extglob # enables extglob option
$ shopt extglob # display current setting
extglob on
$ filename=testfile.cat.exe
$ file=${filename/#(.cat$|.exe$)/}
$ echo $file
testfile.cat
$ filename=testfile.cat.exe # What happens when you don't use $
$ echo ${filename/#(.cat|.exe)/}
testfile.exe
$
Related
This question already has answers here:
How can I use inverse or negative wildcards when pattern matching in a unix/linux shell?
(11 answers)
Closed 2 years ago.
On the command-line *tmp* will match all files with names containing "tmp". Is there a quick way to do the reverse, i.e. match all files with names that don't contain "tmp"?
I figured out how to get ls to do it (ls -I "*tmp*"), but that doesn't help if I want to use some other command rather than ls. Is there a general method?
I forgot to note: I'm using zsh.
It depends on your shell.
In ksh, you can use this:
!(*tmp*)
In bash, the same thing works if you first enable the feature with shopt -s extglob.
In zsh, you can enable the same syntax with setopt ksh_glob, but there's a conflict with another zsh feature that you have to disable with setopt no_bare_glob_qual before the above will actually work. Alternatively, you can just use zsh's native version via setopt extended_glob; the equivalent of the above expression then looks like this:
^*tmp*
When using bash, you can enable extglob with:
shopt -s extglob
then you can use:
!(*tmp*)
that will negate your wildcard condition.
you can finally disable extglob with shopt -u extglob.
This question already has an answer here:
Bash command runs in terminal but not in script file
(1 answer)
Closed 3 years ago.
I have the below command "${CHROMEDRIVER//#([\/])/}" is shell file and on running bash file.sh, it doesn't work:
#!/bin/bash
CHROMEDRIVER="75.0.3770.8/"
echo $CHROMEDRIVER
echo "${CHROMEDRIVER//#([\/])/}"
But, when I run the same cmd "${CHROMEDRIVER//#([\/])/}" in terminal, it works great
This command requires extglob support to be enabled. Presumably you're turning it on (with shopt -s extglob) in your dotfiles, but it's off-by-default in scripts.
See the bash-hackers' wiki on extended pattern language, describing the specific items that require the extglob flag to be enabled; #(...) is among them.
Thus, to fix your script without changing the pattern you're replacing:
#!/usr/bin/env bash
shopt -s extglob
CHROMEDRIVER="75.0.3770.8/"
echo "$CHROMEDRIVER"
echo "${CHROMEDRIVER//#([\/])/}"
...though insofar as your goal is just to strip a trailing slash, use "${CHROMEDRIVER%/}" instead, as described in the bash-hackers' wiki page on parameter expansion.
This question already has answers here:
How to skip the for loop when there are no matching files?
(2 answers)
Closed 3 years ago.
I want to loop over all files matching extension jpg or txt. I use:
for file in myDir/*.{jpg,txt}
do
echo "$file"
done
Problem: If the directory contains no jpg file at all, the loop will have one iteration with output myDir/*.jpg. I thought * will be replaced by an arbitrary file (and if no file exists it cannot be expanded). How can I avoid the unwanted iteration?
Use this to avoid the unwanted iteration:
shopt -s nullglob
From man bash:
nullglob: If set, bash allows patterns which match no files (see Pathname Expansion above) to expand to a null string, rather than themselves.
See: help shopt and shopt
This and a duplicate question both were in context of not just pathname-expansion, but also brace-expansion, and a duplicate asked for POSIX.
The compgen -G does bash --posix compatible pathname-expansion (no brace-expansion) and... you guessed it: yields nothing if there are no matches.
Therefore write a bash --posix function to do it. Brief outline: temporarily use set -f to first do brace-expansion (without pathname-expansion) via an echo, then apply compgen -G to each result for pathname-expansion. Full function left as an exercise.
This question already has answers here:
How to skip the for loop when there are no matching files?
(2 answers)
Closed 3 years ago.
I have this bash "for in" loop that looks for pdf files in a directory and prompt them (simplified for the example)
#!/bin/bash
for pic in "$INPUT"/*.pdf
do
echo "found: ${pic}"
done
This script works well when there are pdf files in $INPUT directory, however, when there are no pdf files in the directory, I get :
found: /home/.../input-folder/*.pdf
Is it the expected behavior ?
How can I deal with it with a for in loop ?
Do I need to use ls or find ?
I tried with and without quotes around "$INPUT". There are no spaces in files names and directory names.
This is the expected behavior. According to the bash man page, in the Pathname Expansion section:
After word splitting, unless the -f option has been set, bash scans each word for the characters *, ?, and [. If one of these characters appears, then the word is regarded as a pattern, and replaced with an alphabetically sorted list of file names matching the pattern. If no matching file names are found, and the shell option nullglob is not enabled, the word is left unchanged.
As a result, if no matches for "$INPUT"/*.pdf are found, the loop will be executed on the pattern itself. But in the next sentence of the man page:
If the nullglob option is set, and no matches are found, the word is removed.
That's what you want! So just do:
#!/bin/bash
shopt -s nullglob
for pic in "$INPUT"/*.pdf
do
echo "found: ${pic}"
done
(But be aware that this may change the behavior of other things in unexpected ways. For example, try running shopt -s nullglob; ls *.unmatchedextension and see what happens.)
I would just add a file exists test like this:
#!/bin/bash
if test -e `echo "$INPUT"/*.pdf | cut -d' ' -f1`
then
for pic in "$INPUT"/*.pdf
do
echo "found: ${pic}"
done
fi
This question already has answers here:
How to skip the for loop when there are no matching files?
(2 answers)
Closed 3 years ago.
I am using bash on UNIX (sparc 10)
for file in $SCPATH/$LIBNAME/*.{gob,c,cpp,h};
do
ln -s $file;
done;
The problem is: if there are no files with extension 'c', it will put ".c" in $file and ln -s will create a link to '.c'.
Is this a know issue? How can I get around it (besides the obvious 'if not *.c' hack).
you need to set nullglob before your loop
shopt -s nullglob
nullglob:
If set, bash allows patterns which match no files
(see Pathname Expansion above) to
expand to a null string, rather than
themselves.
When you're done and want to reset it to the original behavior, use:
shopt -u nullglob
As pointed out by Dennis Williamson in one of the comments.