0

first time on here, I apologize if I missed anything.

I am currently writing a script that searches through each folder and looks for a <filename>.vmdk file, after of which I want to do ls -lah on this file.

Here is my current code for the script.

#!/bin/bash

parse_dir () { for file in ./*/[A-Za-z][!-flat][!-thin].vmdk; do #echo "File Found: "${file//\ /\\ } ls -lah "${file//\ /\\ }" #vmkfstools -i "${file//\ /\\ }" -d thin "${file%.vmdk}-thin.vmdk" #echo "New provisioned file: ${file%.vmdk}-thin.vmdk" done }

parse_dir

This script outputs all of the matched files like this: ls: ./ABC\ Collector/ABC\ Collector.vmdk: No such file or directory

HOWEVER! If I manually type in ls -lah ./ABC\ Collector/ABC\ Collector.vmdk this file exists just fine.

Am I missing something here?

TXJ
  • 3

2 Answers2

3

You don't need to escape the spaces when they're the result of an quoted expansion.

$ touch 'some file.txt'
$ ls -l some\ file.txt 
-rw-r--r-- 1 ilkkachu ilkkachu 0 Sep 17 01:03 some file.txt
$ f='some file.txt'
$ ls -l "$f"
-rw-r--r-- 1 ilkkachu ilkkachu 0 Sep 17 01:03 some file.txt

In fact, as you saw, it doesn't work. Quotes and escapes are only special before variable expansions, quotes that come as result of expansions aren't. Expanding a variable in the shell command line is not the same as just putting the variable contents literally in the same place in the command line. (It's not a macro processor, it's a programming language.)

$ s='foo\ bar'
$ printf ':%s\n' $s
:foo\
:bar
$ printf ':%s\n' "$s"
:foo\ bar

Without quotes, the value of s is split on whitespace (by the contents of IFS), with quotes, it isn't. In neither case does the backslash escape the space.

In a for loop like that, you could just do

for f in ./**/*.vmdk; do
    ls -l "$f"
done

Though note that *[A-Za-z][!-flat][!-thin].vmdk probably doesn't do what you mean, [A-Za-z] matches one character that is a letter, and [!-flat] matches any one character that isn't -, f, l, a, or t. That would e.g. include abc.vmdk, but not bac.vmdk.

Instead, use another condition to rule out the unwanted cases:

for file in ./**/*.vmdk; do
    case "$file" in
        *-flat.vmdk) continue;;
        *-thin.vmdk) continue;;
    esac
    echo "found: $file"
    ls -l "$file"
    new=${file%.vmdk}-thin.vmdk
    # vmkfstools -i "$file" -d thin "$new"
    # echo "New provisioned file: $new"
done

See:

ilkkachu
  • 138,973
  • +1 Thank you!! I have gone with the following https://github.com/txj-xyz/Scripts/blob/master/Bash/convert-to-thin-vmdk.sh – TXJ Sep 17 '20 at 13:08
1

Spaces in filenames will hate you and you will hate them, especially when passing them around from filenames into globs into scripted shell commands. Let find and xargs do the heavy lifting for you:

$  tree
.
+--- ABC Collector
|   +--- ABC Collector-flat.vmdk
|   +--- ABC Collector-thin.vmdk
|   +--- ABC Collector.vmdk
$  find . -type f -name '*.vmdk' -not -name '*-flat.vmdk' -not -name '*-thin.vmdk' -print0 | xargs -0 ls -lah
-rw-r--r--    1 myuser mygroup       0 Sep 16 14:29 ./ABC Collector/ABC Collector.vmdk
DopeGhoti
  • 76,081
  • Thanks for this! I understand this now and how the structure of this works :) +1 – TXJ Sep 17 '20 at 13:09