Bash completion for Camel.CaseNames (cd CCN<tab> => cd Camel.CaseName) - bash

I'm looking for a way to implement completion for file/directory names of the kind
Foo.Bar.Baz/
Foo.Bar.QuickBrown.Fox/
Foo.Bar.QuickBrown.Interface/
Foo.Bar.Query.Impl/
where the completion would work like
~ $ cd QI<tab><tab>
Foo.Bar.QuickBrown.Interface/ Foo.Bar.Query.Impl/
~ $ cd QIm<tab><enter>
~/Foo.Bar.Query.Impl $
However, my simplistic approach of building a glob pattern from the input (eg. QIm -> *Q*I*m) does not exactly work for files / directories sharing the same prefix. In the case above, I get
~ $ cd QI<tab><tab>
Foo.Bar.QuickBrown.Interface/ Foo.Bar.Query.Impl/
~ $ cd Foo.Bar.Qu<tab><tab>
Foo.Bar.QuickBrown.Fox/ Foo.Bar.QuickBrown.Interface/ Foo.Bar.Query.Impl/
i.e. bash replaces the current word with the longest common prefix of the possible completions, which in this case results in larger completion set.
Here's my completion function:
_camel_case_complete()
{
local cur pat
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
pat=$(sed -e 's/\([A-Z]\)/*\1*/g' -e 's/\*\+/*/g' <<< "$cur")
COMPREPLY=( $(compgen -G "${pat}" -- $cur ) )
return 0
}
Any hints how to fix this without breaking normal filename / directory completion?

See following example:
% ls -l
total 20
-rw-r--r-- 1 root root 315 2016-06-02 18:30 compspec
drwxr-xr-x 2 root root 4096 2016-06-02 17:56 Foo.Bar.Baz
drwxr-xr-x 2 root root 4096 2016-06-02 17:56 Foo.Bar.Query.Impl
drwxr-xr-x 2 root root 4096 2016-06-02 17:56 Foo.Bar.QuickBrown.Fox
drwxr-xr-x 2 root root 4096 2016-06-02 17:56 Foo.Bar.QuickBrown.Interface
% cat compspec
_camel_case_complete()
{
local cur=$2
local pat
pat=$(sed -e 's/[A-Z]/*&/g' -e 's/$/*/' -e 's/\*\+/*/g' <<< "$cur")
COMPREPLY=( $(compgen -G "${pat}" ) )
if [[ ${#COMPREPLY[#]} -gt 1 ]]; then
# Or use " " instead of "__"
COMPREPLY[${#COMPREPLY[#]}]="__"
fi
return 0
}
complete -F _camel_case_complete cd
% . ./compspec
% cd QI<TAB><TAB>
__ Foo.Bar.QuickBrown.Interface
Foo.Bar.Query.Impl
% cd QIm<TAB>
% cd Foo.Bar.Query.Impl<SPACE>

Related

Bash script not recognizing existing file

I have to write a simple bash script, which does the compilation of multiple sql scripts which can contain recursive references to the other sql scripts with Oracle SQLPlus convention, so in the end the result will be only one SQL file, containing all statements from all (possibly also recursively) referenced subscripts.
I have created a simple script called oracle-script-compressor.sh with following bash code:
#!/bin/bash
#
#
function err () {
echo $* > "/dev/stderr"
}
function replace_subscripts_placeholders() {
local PROCESSING_FILENAME=$1
echo "-- processing file $PROCESSING_FILENAME"
while read script_line; do
local script_line_NO_LEAD_SPACE="$(echo -e "${script_line}" | sed -e 's/^[[:space:]]*//')"
if [[ $script_line_NO_LEAD_SPACE == \#\#* ]] ; then
local file_name="./${script_line_NO_LEAD_SPACE#\#\#}"
echo "-- found reference to file $file_name"
echo "-- starting with processing, pwd=$PWD"
# for debug purposes:
ls -la
# but this returns always false:
if [ -f "$file_name" ]; then
# so this part is never started:
. replace_subscripts_placeholders $file_name
else
err_msg="WARNING: Could not find the referenced file $file_name"
echo "-- $err_msg"
err $err_msg
fi
else
echo "$script_line"
fi
done < $PROCESSING_FILENAME
}
if test -z "$1" ; then
err "Usage: oracle-script-compressor.sh {file_name_to_process} [> output_file]"
err " Be aware, if the referenced files within {file_name_to_process} are not specified by absolute paths, you have to start the script from corresponding directory."
err " If the part [> output_file] is omitted, then this script writes to standard output"
err " If the part [>> output_file] is used, then the result will be appended to output_file it it exists before the processing"
else
if [ -f "$1" ]; then
replace_subscripts_placeholders $1
else
echo "file $1 does not exist"
fi
fi
and I am stuck on interesting problem - it seems, inside of the function replace_subscripts_placeholders() the file check
if [ -f "$file_name" ]; then
. replace_subscripts_placeholders $file_name
else
err_msg="WARNING: Could not find the referenced file $file_name"
echo "-- $err_msg"
err $err_msg
fi
never goes to the recursion call and even, if I remove the if statement and really call the function in recursion with the correct referenced file name, which exists, it is still not recognized to be found and not passed into the loop over all lines of the referenced file in the recursive call (then the error file not found comes and the loop cannot be executed).
I have added the debug messages into script like mentioned above in the script, but still I am unable to find, why the hell should bash not find the file, if it is in the same directory. The scripts are placed in
user#mycomputer:/tmp/test$ ls -la
celkem 52
drwxr-xr-x 2 user user 4096 zář 14 21:47 .
drwxrwxrwt 38 root root 36864 zář 14 21:48 ..
-rw-r--r-- 1 user user 51 zář 14 21:45 a.sql
-rw-r--r-- 1 user user 51 zář 14 21:45 b.sql
-rw-r--r-- 1 user user 590 zář 14 21:46 start.sql
user#mycomputer:/tmp/test$
and the content of the file start.sql looks like this:
spool output__UpdSch.log
whenever sqlerror exit sql.sqlcode
--
--
PROMPT a.sql - starting
select to_char(current_timestamp,'YYYY-MM-DD HH24:MI:SS.FF3') as START_TIMESTAMP from dual;
##a.sql
PROMPT a.sql - finished
select to_char(current_timestamp,'YYYY-MM-DD HH24:MI:SS.FF3') as FINISH_TIMESTAMP from dual;
--
--
PROMPT b.sql - starting
select to_char(current_timestamp,'YYYY-MM-DD HH24:MI:SS.FF3') as START_TIMESTAMP from dual;
##b.sql
PROMPT b.sql - finished
select to_char(current_timestamp,'YYYY-MM-DD HH24:MI:SS.FF3') as FINISH_TIMESTAMP from dual;
--
--
spool off
and if I execute the script, it seems it decoded the filenames correctly, but there is still the problem in bash - the testing of the file existence returns always false:
user#mycomputer:/tmp/test$ ~/tmp/oracle-script-compressor.sh start.sql
-- processing file start.sql
spool output__UpdSch.log
whenever sqlerror exit sql.sqlcode
--
--
PROMPT a.sql - starting
select to_char(current_timestamp,'YYYY-MM-DD HH24:MI:SS.FF3') as START_TIMESTAMP from dual;
-- found reference to file ./a.sql
-- starting with processing, pwd=/tmp/test
celkem 52
drwxr-xr-x 2 user user 4096 zář 14 21:47 .
drwxrwxrwt 38 root root 36864 zář 14 21:48 ..
-rw-r--r-- 1 user user 51 zář 14 21:45 a.sql
-rw-r--r-- 1 user user 51 zář 14 21:45 b.sql
-rw-r--r-- 1 user user 590 zář 14 21:46 start.sql
-- WARNING: Could not find the referenced file ./a.sql
WARNING: Could not find the referenced file ./a.sql
PROMPT a.sql - finished
select to_char(current_timestamp,'YYYY-MM-DD HH24:MI:SS.FF3') as FINISH_TIMESTAMP from dual;
--
--
PROMPT b.sql - starting
select to_char(current_timestamp,'YYYY-MM-DD HH24:MI:SS.FF3') as START_TIMESTAMP from dual;
-- found reference to file ./b.sql
-- starting with processing, pwd=/tmp/test
celkem 52
drwxr-xr-x 2 user user 4096 zář 14 21:47 .
drwxrwxrwt 38 root root 36864 zář 14 21:48 ..
-rw-r--r-- 1 user user 51 zář 14 21:45 a.sql
-rw-r--r-- 1 user user 51 zář 14 21:45 b.sql
-rw-r--r-- 1 user user 590 zář 14 21:46 start.sql
-- WARNING: Could not find the referenced file ./b.sql
WARNING: Could not find the referenced file ./b.sql
PROMPT b.sql - finished
select to_char(current_timestamp,'YYYY-MM-DD HH24:MI:SS.FF3') as FINISH_TIMESTAMP from dual;
--
--
spool off
user#mycomputer:/tmp/test$
The script has read access to files, everything is in the same folder, where I start the script, It also does not matter, if I reference the file with the current directory prefix "./" or not, it is just never found. Interesting is, the check during the start of the script passes correctly, the only problem is the searching within the function... I have also tried to call the function with preceded "." and without it, it makes no difference... There are also no trailing spaces in the referenced file names... It also makes no difference if I declare the variables inside of the function as local or not.
So I have no idea, what could be the problem, maybe I just looked too long on it and cannot see something - seems it has to be something trivial, like the function is started in some different current directory - (but pwd and ls shows the directory is always correct...???) - Any help or pointers will be appreciated.
Thank you for the comments, it brought me to the solution. I found out, this problem was related to the fact, the test sql files were created on Windows, so they had in the end of line always the <CR><LF> characters. This leads to the fact in the original bash script I posted, the script line
while read script_line
puts into the variable not only the given file name from the line, preceded by the characters ##
##a.sql
but also the <CR> character - in the variable is then the value
##a.sql<CR>
what was the cause the file a.sql etc. could never be found. Of course the character is invisible, so therefore it has not been shown on any debug echoing I made here - I had to put the content of $file_name between some another characters and then i could see it in the echoed test... I made also some other corrections and the final working script looks in the following way, if somebody needs to join referenced SQL scripts into one, this does it:
#!/bin/bash
#
#
function err () {
echo $* > "/dev/stderr"
}
function replace_subscripts_placeholders() {
local PROCESSING_FILENAME=$1
echo "-- processing file $PROCESSING_FILENAME"
while read script_line || [ -n "$script_line" ]; do
local script_line_NO_LEAD_SPACE="$(echo -e "${script_line}" | sed -e 's/^[[:space:]]*//' | sed -e 's/^M$//')"
if [[ $script_line_NO_LEAD_SPACE == \#\#* ]] ; then
local file_name="${script_line_NO_LEAD_SPACE#\#\#}"
echo "-- found reference to file $file_name"
if [ -f "$file_name" ]; then
replace_subscripts_placeholders $file_name
else
err_msg="WARNING: Could not find the referenced file $file_name"
echo "-- $err_msg"
err $err_msg
fi
else
echo "$script_line"
fi
done < $PROCESSING_FILENAME
}
if test -z "$1" ; then
err "Usage: oracle-script-compressor.sh {file_name_to_process} [> output_file]"
err " Be aware, if the referenced files within {file_name_to_process} are not specified by absolute paths, you have to start the script from corresponding directory."
err " If the part [> output_file] is omitted, then this script writes to standard output"
err " If the part [>> output_file] is used, then the result will be appended to output_file it it exists before the processing"
else
if [ -f "$1" ]; then
replace_subscripts_placeholders $1
else
echo "file $1 does not exist"
fi
fi

how can find directory

when starting the tcl script a directory is created via a bash command. at the end of my script i want to read the directory name of the latest dirs. but my script does not find the newest directory but only the 2nd newest
bind pub "-|-" !aa pub:aaa
proc pub:aaa {nick host handle channel arg} {
set home "/home/user"
set bb [exec bash -c "start.sh"]
after 3000
set latest [exec bash -c "ls -td $home/jpg/*/ | head -n1"]
putnow "PRIVMSG $channel :$latest"
}
before starting it has the following folders in the directory:
drwxr-xr-x 2 user user 4096 Jun 24 18:30 aaa
drwxr-xr-x 2 user user 4096 Jun 24 18:14 bbb
after starting it has the following folders in the directory
drwxr-xr-x 2 user user 4096 Jun 24 18:30 aaa
drwxr-xr-x 2 user user 4096 Jun 24 18:14 bbb
drwxr-xr-x 2 user user 4096 Jun 24 18:35 ccc
output is :
<#testbot> aaa
it should be so
<#testbot> ccc
he finds the directory created during which the tcl script is not running
how can I display the newest, newly created directory?
regards
Instead of trying to exec out to a shell to find the most recently modified directory, I'd do it in pure tcl:
proc latest_directory {path {time mtime}} {
set dirs {}
foreach dir [glob -nocomplain -type d $path/*] {
file stat $dir s
lappend dirs $s($time) $dir
}
if {[llength $dirs] == 0} {
error "No directories found in $path"
} else {
return [lindex [lsort -integer -decreasing -stride 2 $dirs] 1]
}
}
# Then in pub:aaa
set latest [latest_directory $home/jpg]
As for why you're not getting ccc... hard to say for sure without seeing your start.sh script, but if it ends up running stuff in the background that continues after it exits, maybe it takes more than 3 seconds to create that directory?

Nested for loops in bash

In the directory from where I am running a bash script,
directory saved in the variable:
ScriptDir=`pwd`
I have the following files:
B3LYP_BOTTOM_FRAGMENT
B3LYP-D3_BOTTOM_FRAGMENT
PBE_BOTTOM_FRAGMENT
LDA_BOTTOM_FRAGMENT
PBE-D3_BOTTOM_FRAGMENT
PBE0_BOTTOM_FRAGMENT
PBE0-DC_BOTTOM_FRAGMENT
254.186305_TOP_FRAGMENT.d12
252.050453_TOP_FRAGMENT.d12
249.921810_TOP_FRAGMENT.d12
247.812353_TOP_FRAGMENT.d12
245.699603_TOP_FRAGMENT.d12
243.644688_TOP_FRAGMENT.d12
241.581529_TOP_FRAGMENT.d12
239.554134_TOP_FRAGMENT.d12
237.467646_TOP_FRAGMENT.d12
235.473555_TOP_FRAGMENT.d12
These files can be classified into two different variables: DIRS and FOLDERS
DIRS="
PBE-D3
PBE
B3LYP
B3LYP-D3
PBE0
PBE0-DC
LDA
"
FOLDERS="
237.467646
239.554134
241.581529
243.644688
245.699603
247.812353
249.921810
252.050453
254.186305
235.473555
"
Given this path: /path/to/target, If I loop over DIRS ($i) and FOLDERS ($j), I would like to end up with the following:
ls -lrth /path/to/target/PBE-D3/scaling_volumes/237.467646
237.467646_TOP_FRAGMENT.d12 # j = 1 on FOLDERS
PBE-D3_BOTTOM_FRAGMENT # i = 1 on DIRS
237.467646.d12
# where `237.467646.d12` is the result of doing:
# cat 237.467646_TOP_FRAGMENT.d12 PBE-D3_BOTTOM_FRAGMENT > 237.467646.d12
ls -lrth /path/to/target/PBE-D3/scaling_volumes/239.554134
239.554134_TOP_FRAGMENT.d12 # j = 2 on FOLDERS
PBE-D3_BOTTOM_FRAGMENT # i = 1 on DIRS
239.554134.d12
ls -lrth /path/to/target/PBE-D3/scaling_volumes/241.581529
241.581529_TOP_FRAGMENT.d12 # j = 3 on FOLDERS
PBE-D3_BOTTOM_FRAGMENT # i = 1 on DIRS
241.581529.d12
# and so on...
# In other words, in this iteration, all the `j`th `FOLDERS` for a given `j`th `DIR`
# For the second `DIR`, again the 1st `FOLDER`:
ls -lrth /path/to/target/PBE/scaling_volumes/237.467646
237.467646_TOP_FRAGMENT.d12 # j = 1 on FOLDERS
PBE_BOTTOM_FRAGMENT # i = 2 on DIRS
237.467646.d12
# and so on
I have written the following script:
DIRS="
PBE-D3
PBE
B3LYP
B3LYP-D3
PBE0
PBE0-DC
LDA
"
FOLDERS="
237.467646
239.554134
241.581529
243.644688
245.699603
247.812353
249.921810
252.050453
254.186305
235.473555
"
ScriptDir=`pwd`
for i in ${DIRS}; do
cd /path/to/target/$i
rm -Rf scaling_volumes
mkdir scaling_volumes
cd scaling_volumes
for j in ${FOLDERS}; do
rm -Rf ${j}
mkdir ${j}
cd $ScriptDir
cp -avr ${j}_TOP_FRAGMENT.d12 /path/to/target/$i/scaling_volumes/${j}
cp -avr ${i}_BOTTOM_FRAGMENT /path/to/target/$i/scaling_volumes/${j}
cd /path/to/target/$i/scaling_volumes/${j}
cat ${j}_TOP_FRAGMENT.d12 ${i}_BOTTOM_FRAGMENT > ${j}.d12
cd $ScriptDir
done
done
For some reason what I receive is:
ls -lrth /path/to/target/PBE-D3/scaling_volumes
235.473555 # Only the last FOLDER has been created
or:
ls -lrth /path/to/target/PBE/scaling_volumes
235.473555 # Only the last FOLDER has been created
where only last jth FOLDER was created
Fail fast, add || exit 1 after cd and mkdir commands
Avoid cd use it only when necessary because paths are absolute
Also script directory can be different from pwd (current working directory), for example if script is called from another directory.
Following #choroba 's great advice, I managed to solve the problem by creating a
scaling=`pwd`
variable, and place it just before the for j in ${FOLDERS} loop, and end this loop with
cd $scaling
However, I am very interested in the || exit 1 approach suggested by #Nahuel Fouilleul but I am afraid I do not know where to start with.
ScriptDir=`pwd`
for i in ${DIRS}; do
cd /home/david/Trabajo/structures/Trial_for_double_for_loop_in_bash/pob_TZVP/Calcite_I/$i
rm -Rf scaling_volumes_from_117.743646
mkdir scaling_volumes_from_117.743646
cd scaling_volumes_from_117.743646
scaling=`pwd`
for j in ${FOLDERS}; do
rm -Rf ${j}
mkdir ${j}
cd $ScriptDir
cp -avr ${j}_TOP_FRAGMENT.d12 /home/david/Trabajo/structures/Trial_for_double_for_loop_in_bash/pob_TZVP/Calcite_I/$i/scaling_volumes_from_117.743646/${j}
cp -avr ${i}_BOTTOM_FRAGMENT /home/david/Trabajo/structures/Trial_for_double_for_loop_in_bash/pob_TZVP/Calcite_I/$i/scaling_volumes_from_117.743646/${j}
cd /home/david/Trabajo/structures/Trial_for_double_for_loop_in_bash/pob_TZVP/Calcite_I/$i/scaling_volumes_from_117.743646/${j}
cat ${j}_TOP_FRAGMENT.d12 ${i}_BOTTOM_FRAGMENT > ${j}.d12
cd $scaling
done
done

mkdir recursion with permissions

It's a simple question, I'm writing a bash script called from cron grouping files in tar file and classifing into a dir structure.
These dir needs a special owner and permissions, and I call mkdir command thru su:
#!/bin/bash
... # shortened code
$PERMS=750
$DIR=/home/luser/0/01/012/0123
$OWNER=luser
... # shortened code
su -c "mkdir -m $PERMS -p $DIR" $OWNER
Output for ll -R /home/luser/0
/home/luser/0:
total 4
drwxr-xr-x 3 luser luser 4096 Jan 7 18:13 01
/home/luser/0/01:
total 4
drwxr-xr-x 3 luser luser 4096 Jan 7 18:13 012
/home/luser/0/01/012:
total 4
drwxr-x--- 2 luser luser 4096 Jan 7 18:13 0123
/home/luser/0/01/012/0123:
total 0
Only the deepest dir has permissions (750) setting rightly.
I don't know how deep it's the last directory and set permissions for all home's file it's too hard (too much files).
PS: I'm googled about that, but I find nothing.
You can restrict the permissions on the parent directories via umask. Here is an example:
PERMS=750
UMASK=$(echo "$PERMS" | tr "01234567" "76543210")
DIR=/home/luser/0/01/012/0123
OWNER=luser
su -c "umask $UMASK; mkdir -m $PERMS -p $DIR" $OWNER
In action:
> PERMS=750
> UMASK=$(echo "$PERMS" | tr "01234567" "76543210")
> (umask $UMASK; mkdir -m $PERMS -p 1/2/3/4)
> ll -R .
.:
drwxr-x--- 3 luser luser 4096 Jan 7 1:38 1/
./1:
drwxr-x--- 3 luser luser 4096 Jan 7 1:38 2/
./1/2:
drwxr-x--- 3 luser luser 4096 Jan 7 1:38 3/
./1/2/3:
drwxr-x--- 2 luser luser 4096 Jan 7 1:38 4/

No response from [ -f /path/to/file ]

I am trying to get my Capistrano deploy script working, but it is not doing the symlinking as it is configured to do as shown below.
set :linked_files, %w{config/database.yml}
set :linked_dirs, %w{log tmp vendor/bundle public/system}
When it runs the related command, I get the following:
WARN [SKIPPING] No Matching Host for /usr/bin/env [ -f /path/to/shared/config/database.yml ]
If I run this command on the server, either through ssh or through logging onto the server and running the command, I get no response from the command.
user: ~
$ [ -f /path/to/shared/config/database.yml ]
user: ~
$
The file does exist in the specified location and has permissions.
user: ~
$ ll /path/to/shared/config/
total 4.0K
drwxrwxr-x 2 user group 33 Nov 30 10:58 .
drwxrwxr-x 7 user group 89 Nov 30 10:58 ..
-rwxrwxr-x 1 user group 805 Nov 30 10:58 database.yml
user: ~
Shouldn't this return a true or a false, instead of nothing? Is there a configuration I may have changed that suppresses the output? I get no response at all whether the file exists or not.
In your response to the actual question you ask, test (which is what [ is an alias for) does in fact not return output to stdout. It returns an exit code.
user: ~
$ [ -f /path/to/shared/config/database.yml ] # if the file exists
user: ~
$ echo $?
0
user: ~
$ [ -f /path/to/shared/config/database.yml ] # if the file does not exist
user: ~
$ echo $?
1
test -f /path/to/file (or [ -f /path/to/file ]) yields an exit code of 0 if the file exists or 1 if it does not. If you want to check that a file is there and echo the path to it, try:
[ -f /path/to/file ] && echo "/path/to/file"

Resources