msys path conversion (or cygpath for msys?) - path

I need to pass /DEF:c:\filepath\myLib.def" command line option from a bash script to MS compiler/linker. The path is generated as part of build process by a bash script. Basically, the argument that my script passes is:
-DEF:/c/filepath/myLib.def
MSYS path conversion can't handle it properly because it doesn't understand /DEF: part. It works if I do
-DEF=/c/filepath/myLib.def
but then ms tools don't understand this parameter. In short, what's the proper way to write that parameter in MSYS bash so that it converts it to proper argument?
On cygwin I could use cygpath, but there is no equivalent, because somebody from msys thinks that it's not needed (even if there are scripts for cygwin that uses cygpath).

Update (Aug-2016):
This question is no longer relevant, as msys2 now comes with cygpath in its installation.
...
I'll summarize my research here.
The cygpath equivalent in MSYS is to use this command:
{ cd /c/some/path && pwd -W; } | sed 's|/|\\|g'
The problem with this approach is that it requires existing path, e.g. the c:\some\path has to be an existing directory; however, real cygpath supports paths that do not exist.
So, if you need to get path to a directory that doesn't exist, then you can fallback to sed conversion of the path:
{ cd 2>/dev/null /c/some/path && pwd -W ||
echo /c/some/path | sed 's|^/\([a-z,A-Z]\)/|\1:/|'; } | sed 's|/|\\|g'
The mouthful of slashes is there to satisfy quoting rules of sed. So, if c:\some\path doesn't exist on your PC, it will try to convert forward to back slashes and replace /c/ with c:\ (or any other drive letter). The only drawback for this is that it won't work correctly non-existing paths that contain a mounted component, such as /bin/does-not-exist or /usr/bin/does-not-exist.
One more approach is to use cygpath from cygwin in MSYS. It seems that cygwin sets global environment variable CYGPATH, that is, you can use it from regular cmd.exe:
%CYGPATH% -w /c/some/path
C:\some\path
or from MSYS:
$CYGPATH -w /c/some/path
C:\some\path
as long as you set to point /c to /cygdrive/c in cygwin.
But this approach will print you /usr located in cygwin installation, not in MSYS.
In short, I think msys should really include real cygpath in the default set of tools just for some cases that aren't handled automatically by msys command line argument conversion logic

use pwd -W
or
download cygpath for msys from here http://mingw.5.n7.nabble.com/enhanced-version-of-cygpath-td28556.html
and use cygpath -wa

Similar to dmitri-rubinstein# above, I've cleaned up the code a bit and added the reverse conversion as well.
winpath() {
if [ ${#} -eq 0 ]; then
: skip
elif [ -f "$1" ]; then
local dirname=$(dirname "$1")
local basename=$(basename "$1")
echo "$(cd "$dirname" && pwd -W)/$basename" \
| sed \
-e 's|/|\\|g';
elif [ -d "$1" ]; then
echo "$(cd "$1" && pwd -W)" \
| sed \
-e 's|/|\\|g';
else
echo "$1" \
| sed \
-e 's|^/\(.\)/|\1:\\|g' \
-e 's|/|\\|g'
fi
}
unixpath() {
echo "$1" \
| sed -r \
-e 's/\\/\//g' \
-e 's/^([^:]+):/\/\1/'
}

I am using this with msysgit:
winpath() {
if [ -z "$1" ]; then
echo "$#"
else
if [ -f "$1" ]; then
local dir=$(dirname "$1")
local fn=$(basename "$1")
echo "$(cd "$dir"; echo "$(pwd -W)/$fn")" | sed 's|/|\\|g';
else
if [ -d "$1" ]; then
echo "$(cd "$1"; pwd -W)" | sed 's|/|\\|g';
else
echo "$1" | sed 's|^/\(.\)/|\1:\\|g; s|/|\\|g';
fi
fi
fi
}

My bash foo is weak and I couldn't get regexes working in bash 3.1 so I hacked out a perl script for it:
#!/bin/env perl
use strict;
my #r;
foreach my $e (#ARGV) {
$e=~s/\//\\/g;
$e=~s/^\\([A-Za-z])\\/\1:\\/;
push #r, $e;
}
print join(" ", #r);

MSYS cygpath
Program
This program convert a DOS path to a UNIX path and vice versa
#!/bin/env perl
# DOS to UNIX path conversion
# © John S. Peterson. License GNU GPL 3.
use strict;
use Getopt::Std;
# usage
if ($#ARGV == -1) {
print 'Usage: cygpath (-w) NAME...
Convert Unix and Windows format paths
Output type options:
-w, --windows print Windows form of NAMEs (C:\WINNT)
';
exit 0;
}
# option
my %opt;
getopts('w', \%opt);
# convert path
my #r;
foreach my $e (#ARGV) {
if ($opt{w}) {
# add drive letter suffix
$e =~ s,^\/([A-Za-z])\/,\1:\/,;
$e =~ s,\/,\\,g;
} else {
$e =~ s,\\,\/,g;
# add leading slash
$e = "/$e";
# remove drive letter suffix
$e =~ s,:,,;
}
push #r, $e;
}
print join("\n", #r);
Compared to Cygwin cygpath
The output from this program is better than the output from Cygwin cygpath in MSYS because
Cygwin cygpath remove the Cygwin home from a converted path, f.e.
cygpath "$CYGWIN/usr/local/bin"
/usr/local/bin
which is a problem because
it's sometimes useful to convert a DOS Cygwin path to a UNIX path for the purpose of copying files from Cygwin to MSYS
This program doesn't remove the Cygwin home
cygpath "$CYGWIN/usr/local/bin"
/c/file/program/cygwin/usr/local/bin
Compared to automatic MSYS path conversion
Manual path conversion has a use in MSYS because
the automatic path conversion is inadequate
for f.e.
devkitPro make

How about this one ?
cmd //c echo <your path>
It may not work always but it is the shortest I found

This works for me
df "$1" | tac >k
read b <k
rm k
set "$1" $b
echo ${1/$7/$2/}
ref

nearly pure GNU bash solution (which is what you commonly run in MSYS) (interestingly not working with MSYS2, leave a comment if you know why):
#!/bin/bash
# check if we have cygpath (cygwin, newer MSYS2), then just use that
which cygpath 1>/dev/null 2>&1
[[ $? = 0 ]] && (cygpath -wa "$1"; exit $?)
# check if it looks like a Windows path, in which case we directly convert and exit
[[ ${1} =~ ^[a-zA-Z]: ]] && \
echo "${1}" | sed -e 's|/|\\|g' -e 's/\(.\)/\u\1/' && exit 0
# split first path entry (if any) with trailing slash and filename
[[ ${1} =~ ^\([/a-zA-Z0-9_.-]\\w*/\)?\(.*\)$ ]]
chk_root="${BASH_REMATCH[1]}"
chk_rest="${BASH_REMATCH[2]}"
# check if the root path exists and more important: let pwd binary resolve the translation according to the mount
chk_winroot="$(cd "${chk_root}." 2>/dev/null && pwd -W)"
[[ "${chk_winroot}" == "" ]] && echo "${chk_root}: No such file or directory" && exit 1
# using substition to replace all / by \ and uppercasing the first character
# pure bash solution; sadly: the first part needs a newer bash than old MSYS have ...
# chk_drv="${chk_winroot:0:1}"
# chk_all="${chk_winroot:1}/${chk_rest}"
# echo "${chk_drv^^}${chk_all//\//\\}"
# ... so fallback to GNU sed
echo "${chk_winroot}/${chk_rest}" | sed -e 's|/|\\|g' -e 's/\(.\)/\u\1/'
There's still an issue with it: if MinGW's fstab contains an entry like /mnt/c the pwd -W of /mnt/. done in this script won't work.
To fix it: replace pwd -W by inspecting $ cat /etc/fstab | cut -d'#' -f1 | grep -v "^\s*$" entries manually and replace the first match - while this will never work for cygwin or msys2 which use a different format this is covered by using cygpath there.

Related

Print file name if grep finds multiple occurrences of a string in file, else exit on failure

File1 contains
hello
hello
I need to write a grep command to print the filename if this file contains more than one "hello". Otherwise, I need grep to exit on failure.
So far I have
grep -c "hello" File1 | grep -v :0
but it outputs
2. How do I get the desired output, which should either be filename File1 or no output at all (from what I understand, no match is a non zero exit code for grep)
with GNU grep for -z:
grep -lz 'hello.*hello' file
e.g.:
$ seq 15 | grep -lz '3.*3'
(standard input)
$ echo $?
0
$ seq 5 | grep -lz '3.*3'
$ echo $?
1
Like this:
#!/bin/bash
count=$(grep -c "hello" "$1")
if ((count > 1)); then
echo "$1"
else
exit 1
fi
Usage:
chmod +x script.sh
./script.sh File1
Explanations:
((...)) is an arithmetic command, which returns an exit status of 0 if the expression is nonzero, or 1 if the expression is zero. Also used as a synonym for "let", if side effects (assignments) are needed. See http://mywiki.wooledge.org/ArithmeticExpression
Using perl in a shell:
perl -0 -le '
my $filename = $ARGV[0];
print $filename if grep { /hello\nhello/ } <>
' file

Makefile multiline command does not work in Docker properly

I am trying to compile HTML from Markdown. My makefile looks like:
MD = pandoc \
--from markdown --standalone
# ...
$(MD_TARGETS):$(TARGET_DIR)/%.html: $(SOURCE_DIR)/%.md
mkdir -p $(#D); \
$(MD) --to html5 $< --output $#; \
sed -i '' -e '/href="./s/\.md/\.html/g' $#
When I run this on local machine everything works.
When I run the same in Docker I get the following error:
mkdir -p /project/docs; \
pandoc --from markdown --standalone --to html5 /project/source/changelog.md --output /project/docs/changelog.html; \
sed -i '' -e '/href="./s/\.md/\.html/g' /project/docs/changelog.html
sed: can't read : No such file or directory
makefile:85: recipe for target '/project/docs/changelog.html' failed
make: *** [/project/docs/changelog.html] Error 2
Consequent call of make gives the same error but with another file:
sed: can't read : No such file or directory
makefile:85: recipe for target '/project/docs/todo.html' failed
Obviously, make somehow tries sed earlier than HTML is done.
But I use multiline syntax ; \ of make so as to avoid using subshell.
I also tried && \ but neither of them works. What should I do?
Unfortunately you have been misled by your "obvious" conclusion Obviously, make somehow tries sed earlier than HTML is done :) That's not at all what the problem is. Review your error message more carefully:
sed: can't read : No such file or directory
Note the can't read :; there's supposed to be a filename there. It should say something like can't read foobar:. So clearly sed is trying to read a file with the name of empty string. Here's the line you're running:
sed -i '' -e '/href="./s/\.md/\.html/g' /project/docs/changelog.html
The clear culprit here is the empty string argument to -i. The sed man page describes the -i option as:
-i[SUFFIX], --in-place[=SUFFIX]
edit files in place (makes backup if SUFFIX supplied)
Note how (a) the SUFFIX is optional, and (b) there's no space between the argument and its option. In your example you added a space, which causes sed to believe that the SUFFIX was not provided and to treat the next argument ('') as the filename to be operated on.
I'm not sure why it worked when you ran it from the command line: I have to assume that your command line version didn't include the empty string or else it didn't include a space between the -i and the ''.
Use one of:
sed -i'' -e '/href="./s/\.md/\.html/g' $#
or
sed --in-place= -e '/href="./s/\.md/\.html/g' $#

Error running egrep on Solaris

Sun OS 5.8
Bash shell script
Oracle 10g database
Error 1 the command executing at the time of the error was egrep ORA-\|TNS-\|PLS-\|Error\|PLW-\|IMP-\|EXP-\|RMAN-\|SQL- alert_work.log > alert.err on line 11
The "egrep" line runs successfully when I run it manually. But in a bash script (cron job) it gets the above error. Here is the script:
#!/bin/bash
SID=$ORACLE_SID
DOMAIN=$(uname -n)
DBALIST='dbak#xxx.com'
YESTERDAY=`TZ=CST+24 date +%Y-%m-%d`
cd $ORACLE_HOME/admin/$SID/bdump
mv alert_${SID}.log alert_work.log
touch alert_${SID}.log
cat alert_work.log >> alert_${SID}.hist
egrep ORA-\|TNS-\|PLS-\|Error\|PLW-\|IMP-\|EXP-\|RMAN-\|SQL- alert_work.log > alert.err
if [ `cat alert.err|wc -l` -gt 0 ]
then
mailx -s "${DOMAIN}.${SID} ALERT LOG ERRORS FOUND" $DBALIST < alert.err.log
fi
/usr/bin/mv alert_work.log $ORACLE_HOME/admin/$SID/bdump/hist/alert_${SID}_${YESTERDAY}.log
exit
I am suspicious of your egrep regular expression, The fact that you have not quoted it, and create a script from within a Bash script leads and then run the script, leads me to think that you will end up with:
egrep ORA-|TNS-|PLS-|Error|PLW-|IMP-|EXP-|RMAN-|SQL- alert_work.log > alert.err
which is not what you intended. Try:
egrep 'ORA-\|TNS-\|PLS-\|Error\|PLW-\|IMP-\|EXP-\|RMAN-\|SQL-' alert_work.log > alert.err
That should preserve the back slashes.

How can I find a Docker image with a specific tag in Docker registry on the Docker command line?

I try to locate one specific tag for a Docker image. How can I do it on the command line? I want to avoid downloading all the images and then removing the unneeded ones.
In the official Ubuntu release, https://registry.hub.docker.com/_/ubuntu/, there are several tags (release for it), while when I search it on the command line,
user#ubuntu:~$ docker search ubuntu | grep ^ubuntu
ubuntu Official Ubuntu base image 354
ubuntu-upstart Upstart is an event-based replacement for ... 7
ubuntufan/ping 0
ubuntu-debootstrap 0
Also in the help of command line search https://docs.docker.com/engine/reference/commandline/search/, no clue how it can work?
Is it possible in the docker search command?
If I use a raw command to search via the Docker registry API, then the information can be fetched:
$ curl https://registry.hub.docker.com//v1/repositories/ubuntu/tags | python -mjson.tool
[
{
"layer": "ef83896b",
"name": "latest"
},
.....
{
"layer": "463ff6be",
"name": "raring"
},
{
"layer": "195eb90b",
"name": "saucy"
},
{
"layer": "ef83896b",
"name": "trusty"
}
]
When using CoreOS, jq is available to parse JSON data.
So like you were doing before, looking at library/centos:
$ curl -s -S 'https://registry.hub.docker.com/v2/repositories/library/centos/tags/' | jq '."results"[]["name"]' |sort
"6"
"6.7"
"centos5"
"centos5.11"
"centos6"
"centos6.6"
"centos6.7"
"centos7.0.1406"
"centos7.1.1503"
"latest"
The cleaner v2 API is available now, and that's what I'm using in the example. I will build a simple script docker_remote_tags:
#!/usr/bin/bash
curl -s -S "https://registry.hub.docker.com/v2/repositories/library/$#/tags/" | jq '."results"[]["name"]' |sort
Enables:
$ ./docker_remote_tags library/centos
"6"
"6.7"
"centos5"
"centos5.11"
"centos6"
"centos6.6"
"centos6.7"
"centos7.0.1406"
"centos7.1.1503"
"latest"
Reference:
jq: https://stedolan.github.io/jq/ | apt-get install jq
I didn't like any of the solutions above because A) they required external libraries that I didn't have and didn't want to install. B) I didn't get all the pages.
The Docker API limits you to 100 items per request. This will loop over each "next" item and get them all (for Python it's seven pages; other may be more or less... It depends)
If you really want to spam yourself, remove | cut -d '-' -f 1 from the last line, and you will see absolutely everything.
url=https://registry.hub.docker.com/v2/repositories/library/redis/tags/?page_size=100 `# Initial url` ; \
( \
while [ ! -z $url ]; do `# Keep looping until the variable url is empty` \
>&2 echo -n "." `# Every iteration of the loop prints out a single dot to show progress as it got through all the pages (this is inline dot)` ; \
content=$(curl -s $url | python -c 'import sys, json; data = json.load(sys.stdin); print(data.get("next", "") or ""); print("\n".join([x["name"] for x in data["results"]]))') `# Curl the URL and pipe the output to Python. Python will parse the JSON and print the very first line as the next URL (it will leave it blank if there are no more pages) then continue to loop over the results extracting only the name; all will be stored in a variable called content` ; \
url=$(echo "$content" | head -n 1) `# Let's get the first line of content which contains the next URL for the loop to continue` ; \
echo "$content" | tail -n +2 `# Print the content without the first line (yes +2 is counter intuitive)` ; \
done; \
>&2 echo `# Finally break the line of dots` ; \
) | cut -d '-' -f 1 | sort --version-sort | uniq;
Sample output:
$ url=https://registry.hub.docker.com/v2/repositories/library/redis/tags/?page_size=100 `#initial url` ; \
> ( \
> while [ ! -z $url ]; do `#Keep looping until the variable url is empty` \
> >&2 echo -n "." `#Every iteration of the loop prints out a single dot to show progress as it got through all the pages (this is inline dot)` ; \
> content=$(curl -s $url | python -c 'import sys, json; data = json.load(sys.stdin); print(data.get("next", "") or ""); print("\n".join([x["name"] for x in data["results"]]))') `# Curl the URL and pipe the JSON to Python. Python will parse the JSON and print the very first line as the next URL (it will leave it blank if there are no more pages) then continue to loop over the results extracting only the name; all will be store in a variable called content` ; \
> url=$(echo "$content" | head -n 1) `#Let's get the first line of content which contains the next URL for the loop to continue` ; \
> echo "$content" | tail -n +2 `#Print the content with out the first line (yes +2 is counter intuitive)` ; \
> done; \
> >&2 echo `#Finally break the line of dots` ; \
> ) | cut -d '-' -f 1 | sort --version-sort | uniq;
...
2
2.6
2.6.17
2.8
2.8.6
2.8.7
2.8.8
2.8.9
2.8.10
2.8.11
2.8.12
2.8.13
2.8.14
2.8.15
2.8.16
2.8.17
2.8.18
2.8.19
2.8.20
2.8.21
2.8.22
2.8.23
3
3.0
3.0.0
3.0.1
3.0.2
3.0.3
3.0.4
3.0.5
3.0.6
3.0.7
3.0.504
3.2
3.2.0
3.2.1
3.2.2
3.2.3
3.2.4
3.2.5
3.2.6
3.2.7
3.2.8
3.2.9
3.2.10
3.2.11
3.2.100
4
4.0
4.0.0
4.0.1
4.0.2
4.0.4
4.0.5
4.0.6
4.0.7
4.0.8
32bit
alpine
latest
nanoserver
windowsservercore
If you want the bash_profile version:
function docker-tags () {
name=$1
# Initial URL
url=https://registry.hub.docker.com/v2/repositories/library/$name/tags/?page_size=100
(
# Keep looping until the variable URL is empty
while [ ! -z $url ]; do
# Every iteration of the loop prints out a single dot to show progress as it got through all the pages (this is inline dot)
>&2 echo -n "."
# Curl the URL and pipe the output to Python. Python will parse the JSON and print the very first line as the next URL (it will leave it blank if there are no more pages)
# then continue to loop over the results extracting only the name; all will be stored in a variable called content
content=$(curl -s $url | python -c 'import sys, json; data = json.load(sys.stdin); print(data.get("next", "") or ""); print("\n".join([x["name"] for x in data["results"]]))')
# Let's get the first line of content which contains the next URL for the loop to continue
url=$(echo "$content" | head -n 1)
# Print the content without the first line (yes +2 is counter intuitive)
echo "$content" | tail -n +2
done;
# Finally break the line of dots
>&2 echo
) | cut -d '-' -f 1 | sort --version-sort | uniq;
}
And simply call it: docker-tags redis
Sample output:
$ docker-tags redis
...
2
2.6
2.6.17
2.8
--trunc----
32bit
alpine
latest
nanoserver
windowsservercore
As far as I know, the CLI does not allow searching/listing tags in a repository.
But if you know which tag you want, you can pull that explicitly by adding a colon and the image name: docker pull ubuntu:saucy
This script (docker-show-repo-tags.sh) should work for any Docker enabled host that has curl, sed, grep, and sort. This was updated to reflect the fact the repository tag URLs changed.
This version correctly parses the "name": field without a JSON parser.
#!/bin/sh
# 2022-07-20
# Simple script that will display Docker repository tags
# using basic tools: curl, awk, sed, grep, and sort.
# Usage:
# $ docker-show-repo-tags.sh ubuntu centos
# $ docker-show-repo-tags.sh centos | cat -n
for Repo in "$#" ; do
URL="https://registry.hub.docker.com/v2/repositories/library/$Repo/tags/"
curl -sS "$URL" | \
/usr/bin/sed -Ee 's/("name":)"([^"]*)"/\n\1\2\n/g' | \
grep '"name":' | \
awk -F: '{printf("'$Repo':%s\n",$2)}'
done
This older version no longer works. Many thanks to #d9k for pointing this out!
#!/bin/sh
# WARNING: This no long works!
# Simple script that will display Docker repository tags
# using basic tools: curl, sed, grep, and sort.
#
# Usage:
# $ docker-show-repo-tags.sh ubuntu centos
for Repo in $* ; do
curl -sS "https://hub.docker.com/r/library/$Repo/tags/" | \
sed -e $'s/"tags":/\\\n"tags":/g' -e $'s/\]/\\\n\]/g' | \
grep '^"tags"' | \
grep '"library"' | \
sed -e $'s/,/,\\\n/g' -e 's/,//g' -e 's/"//g' | \
grep -v 'library:' | \
sort -fu | \
sed -e "s/^/${Repo}:/"
done
This older version no longer works. Many thanks to #viky for pointing this out!
#!/bin/sh
# WARNING: This no long works!
# Simple script that will display Docker repository tags.
#
# Usage:
# $ docker-show-repo-tags.sh ubuntu centos
for Repo in $* ; do
curl -s -S "https://registry.hub.docker.com/v2/repositories/library/$Repo/tags/" | \
sed -e $'s/,/,\\\n/g' -e $'s/\[/\\\[\n/g' | \
grep '"name"' | \
awk -F\" '{print $4;}' | \
sort -fu | \
sed -e "s/^/${Repo}:/"
done
This is the output for a simple example:
$ docker-show-repo-tags.sh centos | cat -n
1 centos:5
2 centos:5.11
3 centos:6
4 centos:6.10
5 centos:6.6
6 centos:6.7
7 centos:6.8
8 centos:6.9
9 centos:7.0.1406
10 centos:7.1.1503
11 centos:7.2.1511
12 centos:7.3.1611
13 centos:7.4.1708
14 centos:7.5.1804
15 centos:centos5
16 centos:centos5.11
17 centos:centos6
18 centos:centos6.10
19 centos:centos6.6
20 centos:centos6.7
21 centos:centos6.8
22 centos:centos6.9
23 centos:centos7
24 centos:centos7.0.1406
25 centos:centos7.1.1503
26 centos:centos7.2.1511
27 centos:centos7.3.1611
28 centos:centos7.4.1708
29 centos:centos7.5.1804
30 centos:latest
I wrote a command line tool to simplify searching Docker Hub repository tags, available in my PyTools GitHub repository. It's simple to use with various command line switches, but most basically:
./dockerhub_show_tags.py repo1 repo2
It's even available as a Docker image and can take multiple repositories:
docker run harisekhon/pytools dockerhub_show_tags.py centos ubuntu
DockerHub
repo: centos
tags: 5.11
6.6
6.7
7.0.1406
7.1.1503
centos5.11
centos6.6
centos6.7
centos7.0.1406
centos7.1.1503
repo: ubuntu
tags: latest
14.04
15.10
16.04
trusty
trusty-20160503.1
wily
wily-20160503
xenial
xenial-20160503
If you want to embed it in scripts, use -q / --quiet to get just the tags, like normal Docker commands:
./dockerhub_show_tags.py centos -q
5.11
6.6
6.7
7.0.1406
7.1.1503
centos5.11
centos6.6
centos6.7
centos7.0.1406
centos7.1.1503
The v2 API seems to use some kind of pagination, so that it does not return all the available tags. This is clearly visible in projects such as python (or library/python). Even after quickly reading the documentation, I could not manage to work with the API correctly (maybe it is the wrong documentation).
Then I rewrote the script using the v1 API, and it is still using jq:
#!/bin/bash
repo="$1"
if [[ "${repo}" != */* ]]; then
repo="library/${repo}"
fi
url="https://registry.hub.docker.com/v1/repositories/${repo}/tags"
curl -s -S "${url}" | jq '.[]["name"]' | sed 's/^"\(.*\)"$/\1/' | sort
The full script is available at: https://github.com/denilsonsa/small_scripts/blob/master/docker_remote_tags.sh
I've also written an improved version (in Python) that aggregates tags that point to the same version: https://github.com/denilsonsa/small_scripts/blob/master/docker_remote_tags.py
Add this function to your .zshrc file or run the command manually:
#usage list-dh-tags <repo>
#example: list-dh-tags node
function list-dh-tags(){
wget -q https://registry.hub.docker.com/v1/repositories/$1/tags -O - | sed -e 's/[][]//g' -e 's/"//g' -e 's/ //g' | tr '}' '\n' | awk -F: '{print $3}'
}
Thanks to this -> How can I list all tags for a Docker image on a remote registry?
For anyone stumbling across this in modern times, you can use Skopeo to retrieve an image's tags from the Docker registry:
$ skopeo list-tags docker://jenkins/jenkins \
| jq -r '.Tags[] | select(. | contains("lts-alpine"))' \
| sort --version-sort --reverse
lts-alpine
2.277.3-lts-alpine
2.277.2-lts-alpine
2.277.1-lts-alpine
2.263.4-lts-alpine
2.263.3-lts-alpine
2.263.2-lts-alpine
2.263.1-lts-alpine
2.249.3-lts-alpine
2.249.2-lts-alpine
2.249.1-lts-alpine
2.235.5-lts-alpine
2.235.4-lts-alpine
2.235.3-lts-alpine
2.235.2-lts-alpine
2.235.1-lts-alpine
2.222.4-lts-alpine
Reimplementation of the previous post, using Python over sed/AWK:
for Repo in $* ; do
tags=$(curl -s -S "https://registry.hub.docker.com/v2/repositories/library/$Repo/tags/")
python - <<EOF
import json
tags = [t['name'] for t in json.loads('''$tags''')['results']]
tags.sort()
for tag in tags:
print "{}:{}".format('$Repo', tag)
EOF
done
For a script that works with OAuth bearer tokens on Docker Hub, try this:
Listing the tags of a Docker image on a Docker hub through the HTTP API
You can use Visual Studio Code to provide autocomplete for available Docker images and tags. However, this requires that you type the first letter of a tag in order to see autocomplete suggestions.
For example, when writing FROM ubuntu it offers autocomplete suggestions like ubuntu, ubuntu-debootstrap and ubuntu-upstart. When writing FROM ubuntu:a it offers autocomplete suggestions, like ubuntu:artful and ubuntu:artful-20170511.1

Why is capistrano interpreting a flag passed with a command to `run` as input?

I'm trying to do this:
run "echo -n 'foo' > bar.txt"
and the contents of bar.txt ends up being:
-n foo \n
(With \n representing an actual newline)
I use run for other commands like rm -rf and, to my knowledge, it works fine.
I just found this in man echo:
Some shells may provide a builtin echo command which is similar or identical to this utility. Most notably, the builtin echo in sh(1) does not accept the -n option. Consult the builtin(1) manual page.
My version of bash has an echo builtin but seems to be respecting the -n flag. It looks like the shell on your deployment machine doesn't, in which case using the full path to the echo binary might do what you want here:
run "/bin/echo -n 'foo' > bar.txt"
It appears as though the -n flag isn't being interpreted as a flag by the shell. If, from the command line, one executes echo -Y hi, the output will be -Y hi.

Resources