awk: runtime error - Compatility between zsh config on MacOS Catalina and zsh config on Debian Buster - awk

I have a zsh config on MacOS Catalina which works well. No, I would like to get the same but for Debian 10 Buster.
The issue occurs in the using of function for PROMPT that displays pink slashes that separate the PATH current working where directories are in blue.
On MacOS, I do it like this (into my .zshrc) :
# Path with colorized forward slash
slash_color() {
dirs | awk -F "/" '{ blue="%{\033[38;5;75m%}"; \
pink="%{\033[38;5;206m%}"; \
for (i=1; i<NF; i++) \
printf blue $i pink "/"; \
printf blue $NF pink; \
}';
}
# Prompt final
PROMPT=$'%F{13}|%F{green}%n#%F{cyan}%m%F{13}|%f%T%F{13}|$(slash_color)%F{13}|%F{7} '
The result looks like for PROMPT :
Now, on Debian Buster, I have copied the ~/.zshrc from MacOS Catalina.
and when PROMPT is displayed, the PATH of current working directory is not displayed (empty) and I get the following error :
awk: run time error: not enough arguments passed to printf("%{%}~%{%}/")
FILENAME="-" FNR=1 NR=1
I don't know why I have this error on Debian and not on MacOS. I suspect this is due to a difference on the working of my slash_color() function but I don't understand the origin.
It seems that a variable is missing in Debian version for awk, but I can't see which one.

Do not do: printf something. Always do printf "%s", something. The awk errors, because you passed invalid printf format specifiers %{ and %}, yet you did not pass any arguments. Do:
printf "%s%s%s/", blue, $i, pink;
I think you can just:
{gsub("/", pink "/" blue)}1

I would use a pre-command hook and simple parameter expansion instead of forking various external programs.
precmd () {
bar='%F{13}|'
prompt="$bar%F{green}%n#%F{cyan}%m$bar%f%T$bar%F{75}"
prompt+=${PWD:gs./.%F{206}/%F{75}}
prompt+="$bar%F{7} "
}
Add this to your .zshrc file, and prompt will be reset prior to displaying it, rather than embedding a shell function in the prompt itself.
PWD is the current working directory. The gs.---.--- expansion modifier replaces each / with %F{206}/%F{75}, using zsh's own color escape sequences rather than using raw ANSI escape sequences.

Related

How to modify sed awk command to work with relative path

Context
I had a SO question successfully answered at https://stackoverflow.com/a/59244265/80353
I have successfully used the command that was given.
cap()(cd /tmp;rm -f *.vtt;youtube-dl --skip-download --write-auto-sub "$1";\
sed '1,/^$/d' *.vtt|sed 's/<[^>]*>//g'|awk -F. 'NR%8==1{printf"%s ",$1}NR%8==3'\
|tee -a "$2")
What does this command do?
This command will download captions for a youtube video as a .vtt file from $1 parameter
then print out the simplified version of the .vtt file into another file that's stated as parameter $2
This works as advertised.
How to call the command
In the terminal I will run the above command once and then run cap $youtube_url $full_path_to_output_file
What changes I would like
Currently, the $2 parameter must be a full path. Also currently, if the $2 parameter doesn't exist, an actual file will be created. What I would like is this behavior remains even for relative path. So hopefully for relative path, this behavior of creating a new empty file still works.
Update
I see that comments are such that there's nothing wrong with the command.
However, I did try running
cap $youtube_url $relative_path_to_a_text_file and it definitely did not work for me in macOS
Perhaps I am missing something else?
Update 2
This is a video of me running the awk sed command . First I did it with just a relative path. No output file shows up in the current working directory. The second shows me typing the full path and it works.
https://www.loom.com/share/1c179506fa5b48b4a3d62c81a9d2a411
I hope this clarifies the question i am raising and the commenters would kindly update their comments based on this video.
EDIT: Adding a solution after OP's comment which do checks inside OP's function itself, warning not tested it though.
cap()(
user_path=$(echo "$path_details" | awk 'match($0,/.*\//){print substr($0,RSTART,RLENGTH)}')
path_details="$2"
PWD=`pwd`
cd "$PWD"
user_path=$(echo "$path_details" | awk 'match($0,/.*\//){print substr($0,RSTART,RLENGTH)}')
if [[ -d "$user_path" ]]
then
echo "Present path $user_path."
##Call your program here....##
cd /tmp;rm -f *.vtt;youtube-dl --skip-download --write-auto-sub "$1";\
sed '1,/^$/d' *.vtt|sed 's/<[^>]*>//g'|awk -F. 'NR%8==1{printf"%s ",$1}NR%8==3'\
|tee -a "$2"
else
echo "NOT present path $user_path."
##Can exit from here. if needed.##
fi
)
I believe OP wants to check directory of relative path passed as 2nd argument, is present or not, if this is the case then one could try following.
cat file.ksh
path_details="$2"
PWD=`pwd`
##Why I am going to your path is, in case you are running this from cron, so in that case you can mention complete path here, rather than pwd as mentioned above.
cd "$PWD"
user_path=$(echo "$path_details" | awk 'match($0,/.*\//){print substr($0,RSTART,RLENGTH)}')
if [[ -d "$user_path" ]]
then
echo "Present path $user_path."
##Call your program here....##
else
echo "NOT present path $user_path."
##Can exit from here. if needed.##
fi
Explanation: Adding detailed explanation for above code.
cat file.ksh ##For OP reference to show content I am using cat script_name here.
path_details="$2" ##Creating variable path_details whose value is $2(2nd argument passed to script)
PWD=`pwd` ##Creating variable PWD whose value is pwd(current working directory).
##Why I am going to your path is, in case you are running this from cron, so in that case you can mention complete path here, rather than pwd as mentioned above.
cd "$PWD" ##Going to current directory, why I did is you can set PWD above variable value as per your need and navigate to that path, this will help in case of script is running from Cron.
user_path=$(echo "$path_details" | awk 'match($0,/.*\//){print substr($0,RSTART,RLENGTH)}') ##Now getting path details from passed 2nd argument for script.
if [[ -d "$user_path" ]] ##Checking if user_path(path value is existing on system)
then
echo "Present path $user_path."
##Call your program here....## ##If path existing then call your program.
else ##If path NOT existing then exit from program or print message up to you :)
echo "NOT present path $user_path."
##Can exit from here. if needed.##
fi ##Closing if condition here.

Csh Variable Expansion Malfunction

I have created a tcsh script with a series of gawk commands in the following form:
gawk -f InputFileName > OutputFileName
After the standard call-in (#!/bin/csh -f), I utilized the following command:
set a = $<
In the InputFileName, I proceed to use ${a}.txt, but it does not even use $a when looking up the input file.
They were initially running fine on Cygwin (Windows); now they are running on a Linux and are presenting problems.
It was a text file compatibility issue.
Since I did not have dos2unix on my computer, I used the following one line of code.
awk '{ sub("\r$", ""); print }' winfile.txt > unixfile.txt

How to configure nano to set tab to 4 spaces except for Makefiles?

Is there a way to tell nano use 4 spaces insead of TABs for every file except for Makefiles?
I use the following in my .bashrc file:
nano() {
[[ -n $1 ]] && [[ `basename "$1"` =~ (Makefile|makefile|GNUmakefile|.+\.mk) ]] && command nano --tabsize 2 "$#" || command nano --tabstospaces --tabsize 4 "$#"
}
The names checked are taken from the What Name to Give Your Makefile chapter of the GNU Make manual and are ordered according to their recommendations.
To keep things simple I only check the first argument. In my case this is sufficient as any other configuration settings are set in .nanorc (nb: you will need to remove set tabstospaces from .nanorc if present).
I've only used this with BASH 4.x and do not yet have enough experience with the shell to know whether I've used anything incompatible with earlier versions.
edit
Also consider the --ignorercfiles command line flag as a way to improve upon this.

Using awk to extract a specific text in a Makefile

I have a config file with this format
foo=bar
fie=boo
..
..
and there is a Makefile, I want to extract a line of config file that have a string 'disk_size' then extract value that is assigned to the variable
this is the line I've used in Makefile
fallocate -l $(shell awk -F= '/disk_size/ { print $2 }' $(conf)) $#
but I receive this error, (the whole line was extracted.)
fallocate -l disk_size=268435456 disk.img
fallocate: invalid length value specified
the awk command work in terminal but it doesn't work in Makefile, why?
tnx
You probably just need to escape the $:
fallocate -l $(shell awk -F= '/disk_size/ { print $$2 }' $(conf)) $#
Make is trying to use the variable $2 rather than passing the string $2 to awk.
If your config file is really this format:
foo = bar
disk_size = 1234
You can directly include it in the Makefile:
# Include configuration file
include $(conf)
target:
fallocate -l $(disk_size)
You can also use the - operator to ignore error of the include command and assign default value if there is no config file.
# Include configuration file
-include $(conf)
# Set default size
disk_size ?= 5678
target:
fallocate -l $(disk_size)

Redirect stderr to stdout in C shell

When I run the following command in csh, I got nothing, but it works in bash.
Is there any equivalent in csh which can redirect the standard error to standard out?
somecommand 2>&1
The csh shell has never been known for its extensive ability to manipulate file handles in the redirection process.
You can redirect both standard output and error to a file with:
xxx >& filename
but that's not quite what you were after, redirecting standard error to the current standard output.
However, if your underlying operating system exposes the standard output of a process in the file system (as Linux does with /dev/stdout), you can use that method as follows:
xxx >& /dev/stdout
This will force both standard output and standard error to go to the same place as the current standard output, effectively what you have with the bash redirection, 2>&1.
Just keep in mind this isn't a csh feature. If you run on an operating system that doesn't expose standard output as a file, you can't use this method.
However, there is another method. You can combine the two streams into one if you send it to a pipeline with |&, then all you need to do is find a pipeline component that writes its standard input to its standard output. In case you're unaware of such a thing, that's exactly what cat does if you don't give it any arguments. Hence, you can achieve your ends in this specific case with:
xxx |& cat
Of course, there's also nothing stopping you from running bash (assuming it's on the system somewhere) within a csh script to give you the added capabilities. Then you can use the rich redirections of that shell for the more complex cases where csh may struggle.
Let's explore this in more detail. First, create an executable echo_err that will write a string to stderr:
#include <stdio.h>
int main (int argc, char *argv[]) {
fprintf (stderr, "stderr (%s)\n", (argc > 1) ? argv[1] : "?");
return 0;
}
Then a control script test.csh which will show it in action:
#!/usr/bin/csh
ps -ef ; echo ; echo $$ ; echo
echo 'stdout (csh)'
./echo_err csh
bash -c "( echo 'stdout (bash)' ; ./echo_err bash ) 2>&1"
The echo of the PID and ps are simply so you can ensure it's csh running this script. When you run this script with:
./test.csh >test.out 2>test.err
(the initial redirection is set up by bash before csh starts running the script), and examine the out/err files, you see:
test.out:
UID PID PPID TTY STIME COMMAND
pax 5708 5364 cons0 11:31:14 /usr/bin/ps
pax 5364 7364 cons0 11:31:13 /usr/bin/tcsh
pax 7364 1 cons0 10:44:30 /usr/bin/bash
5364
stdout (csh)
stdout (bash)
stderr (bash)
test.err:
stderr (csh)
You can see there that the test.csh process is running in the C shell, and that calling bash from within there gives you the full bash power of redirection.
The 2>&1 in the bash command quite easily lets you redirect standard error to the current standard output (as desired) without prior knowledge of where standard output is currently going.
I object the above answer and provide my own. csh DOES have this capability and here is how it's done:
xxx |& some_exec # will pipe merged output to your some_exec
or
xxx |& cat > filename
or if you just want it to merge streams (to stdout) and not redirect to a file or some_exec:
xxx |& tee /dev/null
As paxdiablo said you can use >& to redirect both stdout and stderr. However if you want them separated you can use the following:
(command > stdoutfile) >& stderrfile
...as indicated the above will redirect stdout to stdoutfile and stderr to stderrfile.
xxx >& filename
Or do this to see everything on the screen and have it go to your file:
xxx | & tee ./logfile
What about just
xxx >& /dev/stdout
???
I think this is the correct answer for csh.
xxx >/dev/stderr
Note most csh are really tcsh in modern environments:
rmockler> ls -latr /usr/bin/csh
lrwxrwxrwx 1 root root 9 2011-05-03 13:40 /usr/bin/csh -> /bin/tcsh
using a backtick embedded statement to portray this as follows:
echo "`echo 'standard out1'` `echo 'error out1' >/dev/stderr` `echo 'standard out2'`" | tee -a /tmp/test.txt ; cat /tmp/test.txt
if this works for you please bump up to 1. The other suggestions don't work for my csh environment.