Is it possible to configure zsh to suggest filenames (or anything else) inside of a quoted string?
I've seen this thread on bash: Bash TAB-completion inside double-quoted string
But I'm not sure whether that solution is compatible between shells.
No problem for tab completion inside quotes.
$ touch "spaces in a filename"
$ ls
spaces in a filename
$ ls sp[TAB]
gives ->
$ ls spaces\ in\ a\ filename
$ ls "sp[TAB]
gives ->
$ ls "spaces in a filename"
Related
I have 3 files in 3 different directories and I need to printout only files from DIR 1 and 2
1 /tmp/CDE/fileA.log
2 /tmp/CFGH/fileB.log
3 /tmp/CILM_NO/fileC.log
if I run from bash /bin/ls /tmp/C{[A-Z][A-Z],[A-Z][A-Z][A-Z]}/*.log it works and I get:
/tmp/CDE/fileA.log /tmp/CFGH/fileB.log
if I run ls bash command from script perl:
$cmd=`/bin/ls /tmp/C{[A-Z][A-Z],[A-Z][A-Z][A-Z]}/*.log`;
chomp($cmd);
print "$cmd\n";
I receive:
/bin/ls: cannot access /tmp/C{[A-Z][A-Z],[A-Z][A-Z][A-Z]}/*.log: No such file or directory
It looks I need to escape \{ or \, or \} but got the same output and it does not work
I also tried using quote instead of escaping but still got same error output
It's not a matter of permission, script is 777
Can't sort of it.
(not an answer, an explanation)
In your shell
ls /tmp/C{[A-Z][A-Z],[A-Z][A-Z][A-Z]}/*.log
That uses bash Brace Expansion. Bash will expand that to
ls /tmp/C[A-Z][A-Z]/*.log /tmp/C[A-Z][A-Z][A-Z]/*.log
And then do Filename Expansion
in perl
$cmd=`/bin/ls /tmp/C{[A-Z][A-Z],[A-Z][A-Z][A-Z]}/*.log`;
The backticks will call out to /bin/sh not bash, so the brace expansion will not happen
I would like to capture a directory that contains spaces in a bash variable and pass this to the ls command without surrounding in double quotes the variable deference. Following are two examples that illustrate the problem. Example 1 works but it involves typing double quotes. Example 2 does not work, but I wish it did because then I could avoid typing the double quotes.
Example 1, with quotes surrounding variable, as in the solution to How to add path with space in Bash variable, which does not solve the problem:
[user#machine]$ myfolder=/home/username/myfolder\ with\ spaces/
[user#machine]$ ls "$myfolder"
file1.txt file2.txt file3.txt
Example 2, with quotes part of variable, which also does not solve the problem. According to my understanding, in this example, the first quote character sent to the ls command before the error is thrown:
[user#machine]$ myfolder=\"/home/username/myfolder\ with\ spaces/\"
[user#machine]$ ls $myfolder
ls: cannot access '"/home/username/myfolder': No such file or directory
In example 2, the error message indicates that the first double quote was sent to the ls command, but I want these quotes to be interpreted by bash, not ls. Is there a way I can change the myfolder variable so that the second line behaves exactly as the following:
[user#machine]$ ls "/home/username/myfolder with spaces/"
The goal is to craft the myfolder variable in such a way that (1) it does not need to be surrounded by any characters and (2) the ls command will list the contents of the existing directory that it represents.
The motivation is to have an efficient shorthand to pass long directory paths containing spaces to executables on the command line with as few characters as possible - so without double quotes if that is possible.
Assuming some 'extra' characters prior to the ls command is acceptable:
$ mkdir /tmp/'myfolder with spaces'
$ touch /tmp/'myfolder with spaces'/myfile.txt
$ myfolder='/tmp/myfolder with spaces'
$ myfolder=${myfolder// /?} # replace spaces with literal '?'
$ typeset -p myfolder
declare -- myfolder="/tmp/myfolder?with?spaces"
$ set -xv
$ ls $myfolder
+ ls '/tmp/myfolder with spaces'
myfile.txt
Here's a fiddle
Granted, the ? is going to match on any single character but how likely is it that you'll have multiple directories/files with similar names where the only difference is a space vs a non-space?
The following is a simple test case for what I want to illustrate.
In bash,
# define the function f
f () { ls $args; }
# Runs the command `ls`
f
# Runs the fommand `ls -a`
args="-a"
f
# Runs the command `ls -a -l`
args="-a -l"
f
But in zsh
# define the function f
f () { ls $args }
# Runs the command `ls`
f
# Runs the fommand `ls -a`
args="-a"
f
# I expect it to run `ls -a -l`, instead it gives me an error
args="-a -l"
f
The last line in the zsh on above, gives me the following error
ls: invalid option -- ' '
Try `ls --help' for more information.
I think zsh is executing
ls "-a -l"
which is when I get the same error. So, how do I get bash's behavior here?
I'm not sure if I'm clear, let me know if there is something you want to know.
The difference is that (by default) zsh does not do word splitting for unquoted parameter expansions.
You can enable “normal” word splitting by setting the SH_WORD_SPLIT option or by using the = flag on an individual expansion:
ls ${=args}
or
setopt SH_WORD_SPLIT
ls $args
If your target shells support arrays (ksh, bash, zsh), then you may be better off using an array:
args=(-a -l)
ls "${args[#]}"
From the zsh FAQ:
2.1: Differences from sh and ksh
The classic difference is word splitting, discussed in question 3.1; this catches out very many beginning zsh users.
3.1: Why does $var where var="foo bar" not do what I expect? is the FAQ that covers this question.
From the zsh Manual:
14.3 Parameter Expansion
Note in particular the fact that words of unquoted parameters are not automatically split on whitespace unless the option SH_WORD_SPLIT is set; see references to this option below for more details. This is an important difference from other shells.
SH_WORD_SPLIT
Causes field splitting to be performed on unquoted parameter expansions.
ls *.txt shows all files whose name ends with .txt
However if I do the following on a zsh shell: (on macOS 10.15 Catalina in my case)
a=*.txt
b='*.txt'
c="*.txt"
# trying no quotes, single quotes, double quotes
# although doesn't make any difference here
ls $a
ls $b
ls $c
ls "$a"
ls "$b"
ls "$c"
I'm getting
ls: *.txt: No such file or directory
in all cases. How do I include wildcards in a variable and then have commands like ls actually process it as wildcards, rather than literal characters?
You should use ~(tilde) between $ and variable name
to perform globbing in zsh. That is
ls $~a
You can enable wildcards in zsh by using the command:
unsetopt nomatch
If you want to make the change permanent, put the command above into your .zshrc file.
In tcsh I can extract second path element from the end of path by following way
cd /some/long/directory/structure/path/
set x=`pwd`
echo ${x:h:h:t}
directory
How can I do the same in bash?
I mean , does bash also have this kind of modifiers?
The csh-style modifiers can be used with history expansion (unsurprisingly, because history expansion was borrowed from csh).
$ cd /some/long/directory/structure/path/
$ echo !!:1:h:h:t
echo directory
directory
!!:1 selects word 1 (counting from zero) of the previous command, so the argument to cd.
(echo directory appears on standard error because the shell defaults to displaying the result of history expansion before actually executing the resulting command.)
In a non-interactive bash script, history expansion commands as in #chepner's answer won't normally be available. However, you do have parameter expansions like:
$ cd /some/long//directory///structure/path/
$ set x=$(pwd)
$ echo $x
/some/long/directory/structure/path
$ y=${y%/*/*} # each /* is equivalent to one :h
$ y=${y##*/} # equivalent to :t
$ echo $y
directory
cd /some/long/path/somewhere
x=$PWD
basename "$(dirname "$x")"
> path
dirname gets the absolute path of the parent folder of the argument. basename gets the name of the argument.
Edit: remembered the much better way than I was doing before.