i'm very new to stackoverflow and to bash/python scripting.
I'm in need to resize some Data Terrain Model files (300+) in .tif format to be able to convert 'em into .hgt and i'm able to do it all using gdal tool but only per single file at once.
Guess you alredy spotted where scripting comes in: need to automatize the process for the 300+ files!
So i started looking a bit about how bash works and came out with this:
#!/bin/bash
for filename in "'/home/fenix/1\ Vari\ HDD/MTB/DTM\ Alos/'"*.tif; do
PATH=/usr/bin/ gdalwarp -of Gtiff -ts 3601 3601 $filename.tif "'/home/fenix/1\ Vari\ HDD/MTB/DTM Alos/temp/'"$filename.tif
done
I always used the backslash to move into "spaced" name directories or files but seems not working with scripting.... googleing i found using quotes or double quotes would fix it but still no success
As you have seen in the code above i used double quote, quote and backslash alone and any combination of the 3 but i'm always getting
ERROR 4: '/home/fenix/1: No such file or directory
Why?!?!
Thanks in advance and sorry for my english!
EDIT:
Following tripleee golden suggestions i edited the script like:
#!/bin/bash
PATH=/usr/bin/
for filename in "/home/fenix/1 Vari HDD/MTB/DTM Alos/"*.tif; do
gdalwarp -of Gtiff -ts 3601 3601 "$filename" "/home/fenix/1 Vari HDD/MTB/DTM Alos/temp/${filename##*/}"
done
And worked like a charm!
Your excessive quoting is getting in the way.
#!/bin/bash
for filename in "/home/fenix/1 Vari HDD/MTB/DTM Alos/"*.tif; do
PATH=/usr/bin/ gdalwarp -of Gtiff -ts 3601 3601 "$filename" "${filename##*/}"
done
The string /home/fenix/stuff with spaces can be expressed as either of
/home/fenix/stuff\ with\ spaces
"/home/fenix/stuff with spaces"
'/home/fenix/stuff with spaces'
A backslash or quote within quotes produces a literal backslash or quote, as part of the quoted string. A backslashed backslash or quote similarly produces a literal backslash or quote.
Single quotes are stronger; everything between them is literal. Double quotes allow for variable and backtick expansion, and backslash quoting.
So "'/home/fenix/1\ Vari\ HDD/MTB/DTM\ Alos/'" refers to ./'/home/fenix/1\ Vari\ HDD/MTB/DTM\ Alos/ which probably isn't a valid path, unless the current directory contains a directory whose name is literally a single quote, etc (where I put in the leading ./ just to make this more explicit).
Perhaps a complication is that the quotes inhibit wildcard expansion; so the wildcard *.tif needs to be unquoted. (Well, strictly speaking, only the wildcard needs to be unquoted; *'.tif' or *".tif" or *\.\t\i\f would work, too.)
Notice also that the value of $filename is the full path to each expanded value of the wildcard, with no directory prefix or extension suffix trimmed off or any other magic like that. I have speculatively shown how to pass the last argument as the filename with the directory path trimmed off (the parameter substitution ${variable##pattern} retrieves the value of variable with any prefix matching pattern trimmed off). So the output file should land in the current directory, with inp^t from the wildcard match (hopefully then in a different directory, so you don't end up overwriting your input files).
Finally, observe how we take care to always use double quotes around variables which contain file names. (Failing to do this is a common error even in some fairly popular tutorials. The script will appear to work until you try to handle file names with irregular spacing or literal asterisks, etc.)
The wacky PATH assignment looks weird, too. Does gdalwarp execute external commands, and do you really then want it to only find external commands in /usr/bin? Or perhaps you mean to run /usr/bin/gdalwarp (though setting the correct PATH at the beginning of the script would arguably be better than hardcoding a specific absolute pathname).
Related
#!/bin/bash
documents=("/datalog/AB errors.txt");
/usr/bin/bash /home/user/download.sh "${documents[*]}"
the download.sh SCPs into a server and downloads documents but in the case above there is a space in the file name which leads it to seek /datalog/AB instead of AB errors.txt file.
I thought surrounding in double quotes fixes the space in filename issue. I also tried AB\ errors.txt but that caused the entire bash script to not run.
The form
documents=("/datalog/AB errors.txt")
is bad form (turning string into an array !!!). The correct form for assignment is
documents="/datalog/AB errors.txt"
Always assume full-path variables can have spaces or other characters, and always double-quote such variable instantiations, i.e.
ls "${variable}" .
As Gordon Davisson said, you must code in this fashion at every level of scripting/programming to ensure the embedded special characters don't lead to unexpected results.
I have a bash script that I want to add a backslash in front of all underscores. The script searches for all files in a directory and saves the file name to a variable file. Then in the variable I want to replace every instance of _ with \_.
I have looked at several questions on sed about search and replace as well as how to treat special characters, but none of them seemed to apply correctly to this scenario.
#!/bin/bash
file=some_file_name.f90 # I want this to read as some\_file\_name.f90
# I have tried the following (and some more i didnt keep track of)
fileWithEscapedUnderscores=$(sed 's/_/\\_/g' <<<$file)
fileWithEscapedUnderscores=$(sed 's/_/\_/g' <<<$file)
fileWithEscapedUnderscores=$(sed 's/_/\\\_/g' <<<$file)
fileWithEscapedUnderscores=${file/_/\_/}
It seems like I need to escape the backslash. However, if I do that I can get the backslash but no underscore. I also tried simply inserting a backslash before the underscore, but that also had issues.
The simple and obvious solution is to escape or quote the backslash in the parameter expansion.
You also had the slashes wrong; your attempt would merely replace the first one, with the literal string \_/.
To recap, the syntax is ${variable/pattern/replacement} to replace the first occurrence of pattern, and ${variable//pattern/replacement} to replace all occurrences.
fileWithEscapedUnderscores=${file//_/\\_}
# or
fileWithEscapedUnderscores=${file//_/'\_'}
Your first sed attempt should have worked, too; but avoid calling an external process when you can use shell builtins.
Separately, probably take care to use quotes around variables with file names, though it would not matter in your example; see also When to wrap quotes around a shell variable
I've been trying to write a function for my bash profile for quite some time now.
The problem I'm trying to overcome is I'm usually provided with file paths that include spaces and it's a pain having to go through and escape all the spaces before I try to open it up in terminal.
e.g.
File -> /Volumes/Company/Illustrators/Website Front Page Design.ai
What I'm trying to end up with is '/Volumes/Company/Illustrators/Website\ Front\ Page\ Design.ai' being opened from my terminal.
So far I've managed to escape the spaces out, but I then get the error "The file ..... does not exist."
My code so far is
function opn { open "${1// /\\ }";}
Any help would be very much appreciated.
The important thing to understand is the difference between syntax and literal data.
When done correctly, escaping is syntax: It's read and discarded by the shell. That is, when you run
open "File With Spaces"
or
open File\ With\ Spaces
or even
open File" "With\ Spaces
...the quoting and escaping is parsed and removed by the shell, and the actual operating system call that gets executed is this:
execv("/usr/bin/open", "open", "File With Spaces")
Note that there aren't any backslashes (or literal quotes) in that syscall's arguments! If you put literal backslashes in your data, then you cause this to be run:
/* this is C syntax, so "\\" is a single-character backslash literal */
execv("/usr/bin/open", "open", "File\\ With\\ Spaces")
...and unless there's a file with backslashes in its name, that just doesn't work, giving the "file does not exist" error you report.
So -- just call open with your name in quotes:
open "$1"
...there's no need for an opn wrappper.
Spaces are problematic in filenames because they're part of bash's default IFS (Internal Field Separator), which is used to separate tokens in a command line. That means that by default, when you use command an argument with spaces, the command will receive 4 arguments rather than 1 containing spaces.
I'm guessing you called your opn function in the same way, thus resulting in only the first part of your path as $1.
Hopefully, the fix is easy : enclose your path in quotes so that bash does not interpret the spaces. By using this, the need for your opn function disappears : open "/Volumes/Company/Illustrators/Website Front Page Design.ai" should work just fine.
As per this question How to input a path with a white space? I have declared a directory path like that:
startup='/cygdrive/c/Users/Me/AppData/Roaming/Microsoft/Windows/Start Menu/Programs/Startup'; I tried to wrap the path in to double quotes but it is not working either.
But for some reason when I am typing $startup I am getting an error:
$ $startup
bash: /cygdrive/c/Users/Alex/AppData/Roaming/Microsoft/Windows/Start: No such file or directory
How would u fix that?
Surround your variable with quotes:
$ "$startup"
You should always quote variables to be safe. You can refer to this page for more details.
As it states in the first paragraph: "When referencing a variable, it is generally advisable to enclose its name in double quotes. This prevents reinterpretation of all special characters within the quoted string -- except $, ` (backquote), and \ (escape)."
You can also refer to this page which states: The basic rule of thumb is that you should double-quote every expansion. This prevents unwanted word splitting and globbing. When in doubt, quote it.
I am trying to execute script with commands:
sed -i "USER/c\export USER=${signumid}" .bashrc
sed -i "DEVENVHOME=$/c\export DEVENVHOME=${DEVENVHOME:-/home/${signumid}/CPM_WORKAREA/devenv.x}" .bashrc
I want to replace the line with string "USER" in .bashrc with export USER=${signumid} where $signumid variable is being provided through Cygwin prompt. Similarly I want to replace line with string DEVENVHOME=$ with export DEVENVHOME=${DEVENVHOME:-/home/${signumid}/CPM_WORKAREA/devenv.x} in .bashrc file, where $signumid variable is provided through Cygwin prompt.
But I am getting following errors on Cygwin termminal.:
sed: -e expression #1, char 1: unknown command: `U'
sed: -e expression #1, char 3: extra characters after command
The general syntax of a sed script is a sequence of address command arguments statements (separated by newline or semicolon). The most common command is the s substitution command, with an empty address, so we can perhaps assume that that is what you want to use here. You seem to be attempting to interpolate a shell variable $signumid which adds a bit of a complication to this exposition.
If your strings were simply static text, it would make sense to use single quotes; then, the shell does not change the text within the quotes at all. The general syntax of the s command is s/regex/replacement/ where the slash as the argument separator is just a placeholder, as we shall soon see.
sed -i 's/.*USER.*/export USER=you/
s% DEVENVHOME=\$%export DEVENVHOME=${DEVENVHOME:-/home/you/CPM_WORKAREA/devenv.x}%' .bashrc
This will find any line with USER and substitute the entire line with export USER=you; then find any line which contains DEVENVHOME=$ (with a space before, and a literal dollar character) and replace the matched expression with the long string. Because the substitution string uses slashes internally, we use a different regex separator % -- alternatively, we could backslash-escape the slashes which are not separators, but as we shall see, that quickly becomes untenable when we add the following twist. Because the dollar sign has significance as the "end of line" metacharacter in regular expressions, we backslash-escape it.
I have ignored the c\ in your attempt on the assumption that it is simply a misunderstanding of sed syntax. If it is significant, what do you hope to accomplish with it? c\export is not a valid Bash command, so you probably mean something else, but I cannot guess what.
Now, to interpolate the value of the shell variable signumid into the replacement, we cannot use single quotes, because those inhibit interpolation. You have correctly attempted to use double quotes instead (in your edited question), but that means we have to make some additional changes. Inside double quotes, backslashes are processed by the shell, so we need to double all backslashes, or find alternative constructs. Fortunately for us, the only backslash is in \$ which can equivalently be expressed as [$], so let's switch to that notation instead. Also, where a literal dollar sign is wanted in the replacement string, we backslash-escape it in order to prevent the shell from processing it.
sed -i "s/.*USER.*/export USER=$signumid/
s% DEVENVHOME=[$]%export DEVENVHOME=\${DEVENVHOME:-/home/$signumid/CPM_WORKAREA/devenv.x}%" .bashrc
Equivalenty, you could use single quotes around the parts of the script which are meant to be untouched by the shell, and then put an adjacent double-quoted string around the parts which need interpolation, like
'un$touched*by$(the!shell)'"$signumid"'more$[complex]!stuff'
This final script still rests on a number of lucky or perhaps rather unlucky guesses about what you actually want. On the first line, I have changed just USER to a regular expression which matches the entire line -- maybe that's not what you want? On the other hand, the second line makes the opposite assumption, just so you can see the variations -- it only replaces the actual text we matched. Probably one or the other needs to be changed.
Finally, notice how the two separate sed commands have been conflated into a single script. Many newcomers do not realize that sed is a scripting language which accepts an arbitrary number of commands in a script, and simply treat it as a "replace" program with a funny syntax.
Another common source of confusion is the evaluation order. The shell processes the double-quoted string even before sed starts to execute, so if you have mistakes in the quoting, you can easily produce syntax errors in the sed script which lead to rather uninformative error messages (because what sed tells you in the error message is based on what the script looks like after the shell's substutions). For example, if signumid contains slashes, it will produce syntax errors, because sed will see those as terminating separators for the s/// command. An easy workaround is to switch to a separator which does not occur in the value of signumid.