Using environment variables to form paths in Make and windows - windows

I have a make file and I am trying to use it to copy files to a directory. The path of the directory is stored in an environment variable. The problem is when I run make the C:\Data from the environment variable is interpreted as C:Data. How do I stop this being intrepreted as an escape character?
copyData : buildData
cp Release/*.tbl $(DATA)/index
results in:
cp Release/*.tbl C:\Data/index
cp: `C:Data/index': specified destination directory does not exist
Try `cp --help' for more information.

Actually, using forward slashes is the best, and correct, solution. Windows utilities always support forward slashes so it works, and trying to remember to always quote pathnames to avoid issues with backslashes is a major hassle.
In this case the first thing to note is that the problem is not a problem with make. make is passing the right content to the shell; it's the shell which is parsing the backslash as an escape character.
As I said above the right answer is to use forward slashes BUT if you want to allow people to use backslashes you'll have to go through your makefile and quote all arguments where a backslash might appear. For example:
copyData : buildData
cp Release/*.tbl '$(DATA)'/index
will fix your immediate problem.
If you have just a couple of these variables you could also do something like:
QDATA = '$(DATA)'
then remember to use $(QDATA) where you wanted the quoted value:
copyData : buildData
cp Release/*.tbl $(QDATA)/index
PS. Use forward slashes!! :-)

Related

Running command on windows does not allow quotations

when i run a command on windows 10 command line that requires a path as one of its params, it works if the path is NOT inside a quotation, but if a path has a space in it, i need to wrap it inside quotes so that it treats as one single path, but then it complains that the file in that path does not exists.
For example:
C:/PROJECTS/desktopfiles/public/libs/cpdf/win64/cpdf.exe C:/Users/john/Documents/cat.pdf C:/Users/john/Documents/my_dog.pdf -o C:/Users/john/Documents/cat_dog_Merged.pdf
The above works,
the below doesn't (because there is a space in my dog.pdf)
C:/PROJECTS/desktopfiles/public/libs/cpdf/win64/cpdf.exe C:/Users/john/Documents/cat.pdf C:/Users/john/Documents/my dog.pdf -o C:/Users/john/Documents/cat_dog_Merged.pdf
You could try to replace spaces with a question mark. The question mark is a wildcard to match "any single character", which would be a space in your case. Like this: my?dog.pdf. Just make sure that there is no other file matching this pattern. But the system should give you some error message then (which might or might not point to the root of the problem).
Another solution that comes to my mind is a batch file that renames the files in question automatically (replacing spaces with underscores) and renames them back after the pdf merge.

How to obtain the full PATH, *allowing* for symbolic links

I have written bash scripts that accept a directory name as an argument. A single dot ('.') is a valid directory name, but I sometimes need to know where '.' is. The readlink and realpath commands provide a resolved path, which does not help because I need to allow for symbolic links.
For example, the resolved path to the given directory might be something like /mnt/vol_01/and/then/some, whereas the script is called with '.' where '.' is /app/then/some (a sym link which would resolve to the first path I gave).
What I have done to solve my problem is use cd and pwd in combination to provide the full path I want, and it seems to have worked OK so far.
A simplified example of a script:
DEST_DIR=$1
# Convert the given destination directory to a full path, ALLOWING
# for symbolic links. This is necessary in cases where '.' is
# given as the destination directory.
DEST_DIR=$(cd $DEST_DIR && pwd -L)
# Do stuff in $DEST_DIR
My question is: is my use of cd and pwd the best way to get what I want? Or is there a better way?
If all you want to do is to make an absolute path that has minimal changes from a relative path then a simple, safe, and fast way to to it is:
[[ $dest_dir == /* ]] || dest_dir=$PWD/$dest_dir
(See Correct Bash and shell script variable capitalization for an explanation of why dest_dir is preferable to DEST_DIR.)
The code above will work even if the directory doesn't exist (yet) or if it's not possible to cd to it (e.g. because its permissions don't allow it). It may produce paths with redundant '.' components, '..' components, and redundant slashes (`/a//b', '//a/b/', ...).
If you want a minimally cleaned path (leaving symlinks unresolved), then a modified version of your original code may be a reasonable option:
dest_dir=$(cd -- "$dest_dir"/ && pwd)
The -- is necessary to handle directory names that begin with '-'.
The quotes in "$dest_dir" are necessary to handle names that contain whitespace (actually $IFS characters) or glob characters.
The trailing slash on "$dest_dir"/ is necessary to handle a directory whose relative name is simply -.
Plain pwd is sufficient because it behaves as if -L was specified by default.
Note that the code will set dest_dir to the empty string if the cd fails. You probably want to check for that before doing anything else with the variable.
Note also that $(cd ...) will create a subshell with Bash. That's good in one way because there's no need to cd back to the starting directory afterwards (which may not be possible), but it could cause a performance problem if you do it a lot (e.g. in a loop).
Finally, note that the code won't work if the directory name contains one or more trailing newlines (e.g. as created by mkdir $'dir\n'). It's possible to fix the problem (in case you really care about it), but it's messy. See How to avoid bash command substitution to remove the newline character? and shell: keep trailing newlines ('\n') in command substitution. One possible way to do it is:
dest_dir=$(cd -- "$dest_dir"/ && printf '%s.' "$PWD") # Add a trailing '.'
dest_dir=${dest_dir%.} # Remove the trailing '.'

Folder with space in name not recognized

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).

How can I scp a file with a colon in the file name?

I'm trying to copy a file using scp in bash with a colon (:) character in the source filename. The obfuscated version of my command I'm using is:
scp file\:\ name.mp4 user#host:"/path/to/dest"
I get this error:
ssh: Could not resolve hostname Portal 2: Name or service not known
I know I could just rename the file and remove the :, but I'd like to know if it's possible to escape the colon.
Not quite a bash escaping problem, it's scp treating x: as a [user#]host prefix, try:
scp ./file:\ name.mp4 user#host:"/path/to/dest"
Using relative (e.g. ./) or fully qualified paths (/path/to/source) prevents this behaviour - the presence of / before a : causes OpenSSH to stop checking for a possible host: or user#host: prefix).
OpenSSH's scp only special-cases filenames that start with a colon allowing those to work without problems, it has no support for escaping a : in the normal sense, and has no other notion of valid hostnames so almost any filename with a : can cause this (or equivalent IPv6 behaviour if [ ] are found before :).
This can also affect other programs, e.g. rsync, the same workaround applies there.
(Due to OpenSSH's simplistic parsing of [] enclosed IPv6 addresses, you can successfully scp files containing : which start with [, or contain #[ before the : and do not contain ]: , but that's not generally useful ;-)
(The below text was written when the original question was How do I escape a colon in bash? It applies to that situation, but not to scp as no amount of shell escaping will help there.)
To answer the question about how to escape :, you don't need to, but "\:" works. Places that a : is used:
the null command :, no need to escape, though you can, just like \e\c\h\o foo it has no effect on the command ("no effect" is not completely true, if you escape one or more characters it will prevent an alias being matched, and you can alias :)
PATH (and others, CDPATH, MAILPATH) escaping the values has no useful effect (I have been unable to run a program in my PATH from a directory containing a :, which is a little unexpected)
parameter expansion ${name:-x} and more, name must be [a-zA-Z_][a-zA-Z0-9_], so no need to escape variables names, and since there's no ambiguity, no need to escape subsequent : in the other variations of parameter expansion
? : trinary operates only on variables and numbers, no need to escape
== and =~ with classes in the pattern like [[:digit:]], you can escape with \: but I'm at a loss as to how that might ever be useful...
within command or function names, no need to escape, \: has no useful effect
(Note that the null command is just :, you can have a command or function named like ":foo" and it can be invoked without escaping, in this respect it's different to # where a command named #foo would need to be escaped.)
I try using fully qualified paths as #mr.spuratic answer but not work and in my situation, I have to use absolute paths, this is my solution:
scp `hostname`:/root/this/is/test/file.txt user#host:"/path/to/dest"

Incorrect #INC in Activestate Perl in Windows

I am using ActiveState perl with Komodo Edit.
I am getting the following error.
Can't locate MyGengo.pm in #INC (#INC contains: C:/Perl/site/lib C:/Perl/lib .)
at D:\oDesk\MyGengo Integration\sample line 6.
Why is the interpreter looking in C:/Perl/lib instead of C:\Perl\lib?
Doesn’t it know that it is Windows and not Linux?
EDIT
I resolved the problem by copying the .pm file in C:\Perl\lib directory. I think, the issue happened since this module was manually downloaded. PPM install would copy the .pm file to the lib directory.
As far as Windows is concerned, C:/Perl/lib and C:\Perl\lib are the same directory.
The perlport documentation notes (emphasis added)
DOS and Derivatives
Perl has long been ported to Intel-style microcomputers running under systems like PC-DOS, MS-DOS, OS/2, and most Windows platforms you can bring yourself to mention (except for Windows CE, if you count that). Users familiar with COMMAND.COM or CMD.EXE style shells should be aware that each of these file specifications may have subtle differences:
my $filespec0 = "c:/foo/bar/file.txt";
my $filespec1 = "c:\\foo\\bar\\file.txt";
my $filespec2 = 'c:\foo\bar\file.txt';
my $filespec3 = 'c:\\foo\\bar\\file.txt';
System calls accept either / or \ as the path separator. However, many command-line utilities of DOS vintage treat / as the option prefix, so may get confused by filenames containing /. Aside from calling any external programs, / will work just fine, and probably better, as it is more consistent with popular usage, and avoids the problem of remembering what to backwhack and what not to.
Your comment shows that you’re using mygengo-perl-new but have it installed in C:\Perl\lib\MyGengo\mygengo-api\nheinric-mygengo-perl-new-ce194df\mygengo. This is an unusual location to install the module. The way the module is written, it expects mygengo.pm to be in one of the directories named in #INC. Client code then pulls it in with
use mygengo;
My suggestion is to move mygengo.pm from C:\Perl\lib\MyGengo\mygengo-api\nheinric-mygengo-perl-new-ce194df\mygengo to C:\Perl\site\lib.
As an alternative if you are using mygengo as part of another package that you’re developing, you could drop mygengo in your source tree, perhaps as a git submodule. Don’t forget to add use lib 'mygengo'; if you do it this way.
For full details, read about the #INC search process in the perlfunc documentation on require and the extra semantics for modules via use.
General advice on slashes versus backslashes
Even if your code will run on Windows only, prefer using forward-slash as the separator in hardcoded paths. Backslash is an escape character in the Perl language, so you have to think more carefully about it. In double-quoted strings, you have to remember to escape the escape character to get its ordinary meaning, e.g.,
# my $dir = "C:\Perl\lib"; # oops, $path would be 'C:Perlib'
$dir = "C:\\Perl\\lib";
The situation can be a little nicer inside single-quoted strings. Setting $dir as in
$dir = 'C:\Perl\lib';
does what you expect, but say you want $dir to have a trailing slash.
$dir = 'C:\Perl\lib\';
Now you have a syntax error.
Can't find string terminator "'" anywhere before EOF at dirstuff line n.
You may want to interpolate another value into $dir.
$dir = 'C:\Perl\lib\$module'; # nope
Oh yeah, you need double-quotes for interpolation.
$dir = "C:\Perl\lib\$module"; # still not right
After headscratching and debugging
$dir = "C:\\Perl\\lib\\$module"; # finally
Backslash is therefore more mistake-prone and a maintenance irritant. Forward slash is an ordinary character inside both single- and double-quoted strings, so it almost always means what you expect.
As perlport notes, the Windows command shell treats forward slash as introducing options and backslash as path separators. If you cannot avoid the shell, then you may be forced to deal with backslashes.

Resources