Reorder cert in cert store within shell [duplicate] - bash

I'm working with OpenSSL and need a sane default list of CAs. I'm using Mozilla's list of trusted CAs, as bundled by cURL. However, I need to split this bundle of CA certs, because the OpenSSL documentation says:
If CApath is not NULL, it points to a directory containing CA certificates in PEM format. The files each contain one CA certificate. The files are looked up by the CA subject name hash value, which must hence be available.
For example, using the ca-bundle.crt file directly works fine:
openssl-1.0.1g> ./apps/openssl s_client -connect www.google.com:443 -CAfile /home/user/certs/ca-bundle.crt
...
Verify return code: 0 (ok)
---
DONE
But specifying the directory containing the ca-bundle.crt file does not work:
openssl-1.0.1g> ./apps/openssl s_client -connect www.google.com:443 -CApath /opt/aspera/certs
Verify return code: 20 (unable to get local issuer certificate)
---
DONE
I presume this is because my folder doesn't adhere to what the documentation asks for (namely, a directory containing CA certs in PEM format, with each file containing one cert, named by hash value). My directory just has the single bundle of certs.
How can I split my bundle of certs to adhere to OpenSSL's request that each cert be in an individual file? Bonus points if the hashing can be done too (though if needed I could write a script to do that myself if all the certs are in individual files).

You can split the bundle with awk, like this, in an appropriate directory:
awk 'BEGIN {c=0;} /BEGIN CERT/{c++} { print > "cert." c ".pem"}' < ca-bundle.pem
Then, create the links OpenSSL wants by running the c_rehash utility that comes with OpenSSL:
c_rehash .
Note: use 'gawk' on non linux-platforms - as above relies on a GNU specific feature.

Just to give an alternative; facing the same issue I ended up with csplit:
csplit -k -f bar foo.pem '/END CERTIFICATE/+1' {10}

If you want to get a single certificate out of a multi-certificate PEM, try:
$ awk '/subject.*CN=host.domain.com/,/END CERTIFICATE/' INPUT.PEM
source

The following Ruby-script will split the bundle (with one or more certificates in it) into files named after the hashes -- side-stepping the c_rehash step in most cases.
To use, cd into the right directory (such as /etc/ssl/certs/) and run the script with the path to your certificate bundle as the sole argument. For example: ruby /tmp/split-certificates.rb ca-root-nss.crt.
#!/usr/bin/env ruby
require 'openssl'
blob = IO.binread(ARGV[0]) # Read the entire file at once
DELIMITER = "\n-----END CERTIFICATE-----\n"
blobs = blob.split(DELIMITER)
blobs.each do |blob|
blob.strip!
blob += DELIMITER # Does not break DER
begin
cert = OpenSSL::X509::Certificate.new blob
rescue
puts "Skipping what seems like junk"
next
end
begin
# XXX Need to handle clashes, suffix other than 0
filename=sprintf("%x.0", cert.subject.hash)
File.open(filename,
File::WRONLY|File::CREAT|File::EXCL) do |f|
f.write(blob)
end
rescue Errno::EEXIST
puts "#{filename} already exists, skipping"
end
end

Here is mine in Perl (so much code, but I like gonzo programming):
#!/usr/bin/perl -w
# -------
# Split "certificate bundles" like those found in /etc/pki/tls/certs into
# individual files and append the X509 cleartext description to each file.
#
# The file to split is given on the command line or piped via STDIN.
#
# Files are simply created in the current directory!
#
# Created files are named "certificate.XX" or "trusted-certificate.XX",
# with XX an index value.
#
# If a file with the same name as the output file already exists, it is not
# overwritten. Instead a new name with a higher index is tried.
#
# This works for bundles of both trusted and non-trusted certificates.
#
# See http://tygerclan.net/?q=node/49 for another program of this kind,
# which sets the name of the split-off files in function of the subject
# -------
my #lines = <> or die "Could not slurp: $!";
my $state = "outside"; # reader state machine state
my $count = 0; # index of the certificate file we create
my $fh; # file handle of the certificate file we create
my $fn; # file name of the certificate file we create
my $trusted; # either undef or "TRUSTED" depend on type of certificate
for my $line (#lines) {
chomp $line;
if ($state eq "outside") {
if ($line =~ /^(-----BEGIN (TRUSTED )?CERTIFICATE-----)\s*$/) {
my $marker = $1;
$trusted = $2;
$state = "inside";
my $created = 0;
my $prefix = "";
if ($trusted) {
$prefix = "trusted-"
}
while (!$created) {
$fn = "${prefix}certificate.$count";
$count++;
if (-f $fn) {
# print STDERR "File '$fn' exists; increasing version number to $count\n";
}
else {
print STDERR "Certificate data goes to file '$fn'\n";
open($fh,">$fn") || die "Could not create file '$fn': $!\n";
$created = 1;
print $fh "$marker\n"
}
}
}
else {
print STDERR "Skipping line '$line'\n"
}
}
else {
if ($line =~ /^(-----END (TRUSTED )?CERTIFICATE-----)\s*$/) {
my $marker = $1;
my $trustedCheck = $2;
if (!((($trusted && $trustedCheck) || (!$trusted && !$trustedCheck)))) {
die "Trusted flag difference detected\n"
}
$state = "outside";
print $fh "$marker\n";
print STDERR "Closing file '$fn'\n";
close $fh;
# Append x509 cleartext output by calling openssl tool
`openssl x509 -noout -text -in '$fn' >> '$fn'`;
if ($? != 0) {
die "Could not run 'openssl x509' command: $!\n";
}
}
else {
print $fh "$line\n"
}
}
}
if ($state eq "inside") {
die "Last certificate was not properly terminated\n";
}

Related

Perl: Umlaut-issue with filenames in windows

I wrote a program in perl that manipulates (create, delete, open, close, read, write, copy, etc.) files and directories. It does this very well when running on Linux (Ubuntu) and also on macOS. But it has to do the same job under windows too, and there I have problems with the encodings of file names that contain characters other than ASCII (for example German Umlauts, but also any other non-ASCII characters).
Since my original program is way too big, I created a shorter program for testing.
This is the shortened equivalent of my first, naive version of my perl program (the program file itself is encoded as UTF-8):
#!/usr/bin/perl -w
use strict;
use warnings;
my $filename = 'FäöüßÄÖÜẞçàéâœ.txt';
my $text = 'TäöüßÄÖÜẞçàéâœ';
my $dirname = 'DäöüßÄÖÜẞçàéâœ';
# list all files in the parent directory before any action -------------
listDirectory('.');
# create file and write into file --------------------------------------
print "Going to open file $filename for writing ... ";
if (open(my $fileHandle, '>', $filename)) {
print "done successfully\n";
print "Going to write text '$text' into file $filename ... ";
if (print $fileHandle $text."\n") {
print "done successfully\n";
} else {
errorExit("failed to write into file", __LINE__);
}
close($fileHandle);
} else {
errorExit("failed to open file for writing", __LINE__);
}
# create a new directory -----------------------------------------------
print "Going to create directory $dirname ... ";
if (mkdir($dirname)) {
print "done successfully\n";
} else {
errorExit("failed to create directory", __LINE__);
}
# list all files in the parent directory again -------------------------
listDirectory('.');
# read file ------------------------------------------------------------
print "Going to open file $filename for reading ... ";
if (open(my $fileHandle, '<', $filename)) {
print "done successfully\n";
print "Going to list content of file $filename:\n";
print "--- begin of content ---\n";
while (my $row = <$fileHandle>) {
chomp $row;
print "$row\n";
}
print "--- end of content ---\n\n";
close($fileHandle);
} else {
errorExit("failed to open file for reading", __LINE__);
}
# list all files in the newly created directory ------------------------
listDirectory($dirname);
# end ------------------------------------------------------------------
print "normal end of execution\n";
exit(0);
# subroutines ==========================================================
# list all files in a directory ----------------------------------------
sub listDirectory {
my $dir = shift;
my $dirname = $dir eq '.' ? 'parent directory' : $dir;
print "Content of $dirname\n";
if (opendir (my $dirHandle, $dir)) {
print "--- begin of content of $dirname ---\n";
while (my $file = readdir($dirHandle)) {
print "$file\n";
}
print "--- end of content of $dirname ---\n\n";
closedir($dirHandle);
} else {
errorExit("failed to open $dirname", __LINE__);
}
}
# Error exit -----------------------------------------------------------
sub errorExit {
my $message = shift;
my $line = shift;
print "Error before line $line:\n";
print "program message: $message\n";
print "system message: $!\n";
print "premature end of execution\n";
exit(0);
}
Output of my program in macOS and in Linux (Ubuntu):
Content of parent directory
--- begin of content of parent directory ---
.
..
testUmlaut.pl
--- end of content of parent directory ---
Going to open file FäöüßÄÖÜẞçàéâœ.txt for writing ... done successfully
Going to write text 'TäöüßÄÖÜẞçàéâœ' into file FäöüßÄÖÜẞçàéâœ.txt ... done successfully
Going to create directory DäöüßÄÖÜẞçàé✠... done successfully
Content of parent directory
--- begin of content of parent directory ---
.
..
testUmlaut.pl
FäöüßÄÖÜẞçàéâœ.txt
DäöüßÄÖÜẞçàéâœ
--- end of content of parent directory ---
Going to open file FäöüßÄÖÜẞçàéâœ.txt for reading ... done successfully
Going to list content of file FäöüßÄÖÜẞçàéâœ.txt:
--- begin of content ---
TäöüßÄÖÜẞçàéâœ
--- end of content ---
Content of DäöüßÄÖÜẞçàéâœ
--- begin of content of DäöüßÄÖÜẞçàé✠---
.
..
--- end of content of DäöüßÄÖÜẞçàé✠---
normal end of execution
This is the expected output.
But I get this when I execute this program on a windows machine:
Content of parent directory
--- begin of content of parent directory ---
.
..
testUmlaut.pl
--- end of content of parent directory ---
Going to open file F├ñ├Â├╝├ƒ├ä├û├£ß║×├º├á├®├ó┼ô.txt for writing ... done successfully
Going to write text 'T├ñ├Â├╝├ƒ├ä├û├£ß║×├º├á├®├ó┼ô' into file F├ñ├Â├╝├ƒ├ä├û├£ß║×├º├á├®├ó┼ô.txt ... done successfully
Going to create directory D├ñ├Â├╝├ƒ├ä├û├£ß║×├º├á├®├ó┼ô ... done successfully
Content of parent directory
--- begin of content of parent directory ---
.
..
testUmlaut.pl
F├ñ├Â├╝├ƒ├ä├û├£ß║×├º├á├®├ó┼ô.txt
D├ñ├Â├╝├ƒ├ä├û├£ß║×├º├á├®├ó┼ô
--- end of content of parent directory ---
Going to open file F├ñ├Â├╝├ƒ├ä├û├£ß║×├º├á├®├ó┼ô.txt for reading ... done successfully
Going to list content of file F├ñ├Â├╝├ƒ├ä├û├£ß║×├º├á├®├ó┼ô.txt:
--- begin of content ---
T├ñ├Â├╝├ƒ├ä├û├£ß║×├º├á├®├ó┼ô
--- end of content ---
Content of D├ñ├Â├╝├ƒ├ä├û├£ß║×├º├á├®├ó┼ô
--- begin of content of D├ñ├Â├╝├ƒ├ä├û├£ß║×├º├á├®├ó┼ô ---
.
..
--- end of content of D├ñ├Â├╝├ƒ├ä├û├£ß║×├º├á├®├ó┼ô ---
normal end of execution
So, all filenames are written with a wrong encoding. Also in the explorer you see misscoded filenames for the new file and directory. But although the text file contains the correct content, my program displays it wrong.
So, I fiddeled around with my program until I got a version, that produces the correct output (identical to the output of the first naïve version under macOS and Linux)).
But in the file system the filenames are still wrong:
13.01.2020 17:36 <DIR> .
10.01.2020 14:46 <DIR> ..
13.01.2020 18:23 2 970 testUmlaut.pl
13.01.2020 18:23 30 FäöüßÄÖÜẞçà éâœ.txt
13.01.2020 18:23 <DIR> DäöüßÄÖÜẞçà éâœ
Here is the code of the new version of my program:
#!/usr/bin/perl -w
use strict;
use warnings;
use utf8;
use Encode;
if ($^O eq 'MSWin32') {
require Win32::Console;
Win32::Console::OutputCP(65001);
}
binmode STDOUT, ":utf8";
my $filename = 'FäöüßÄÖÜẞçàéâœ.txt';
my $text = 'TäöüßÄÖÜẞçàéâœ';
my $dirname = 'DäöüßÄÖÜẞçàéâœ';
# list all files in the parent directory before any action -------------
listDirectory('.');
# create file and write into file --------------------------------------
print "Going to open file $filename for writing ... ";
if (open(my $fileHandle, '>:encoding(UTF-8)', $filename)) {
print "done successfully\n";
print "Going to write text '$text' into file $filename ... ";
if (print $fileHandle $text."\n") {
print "done successfully\n";
} else {
errorExit("failed to write into file", __LINE__);
}
close($fileHandle);
} else {
errorExit("failed to open file for writing", __LINE__);
}
# create a new directory -----------------------------------------------
print "Going to create directory $dirname ... ";
if (mkdir($dirname)) {
print "done successfully\n";
} else {
errorExit("failed to create directory", __LINE__);
}
# list all files in the parent directory again -------------------------
listDirectory('.');
# read file ------------------------------------------------------------
print "Going to open file $filename for reading ... ";
if (open(my $fileHandle, '<:encoding(UTF-8)', $filename)) {
print "done successfully\n";
print "Going to list content of file $filename:\n";
print "--- begin of content ---\n";
while (my $row = <$fileHandle>) {
chomp $row;
print "$row\n";
}
print "--- end of content ---\n\n";
close($fileHandle);
} else {
errorExit("failed to open file for reading", __LINE__);
}
# list all files in the newly created directory ------------------------
listDirectory($dirname);
# end ------------------------------------------------------------------
print "normal end of execution\n";
exit(0);
# subroutines ==========================================================
# list all files in a directory ----------------------------------------
sub listDirectory {
my $dir = shift;
my $dirname = $dir eq '.' ? 'parent directory' : $dir;
print "Content of $dirname\n";
if (opendir (my $dirHandle, $dir)) {
print "--- begin of content of $dirname ---\n";
while (my $file = decode_utf8(readdir($dirHandle))) {
print "$file\n";
}
print "--- end of content of $dirname ---\n\n";
closedir($dirHandle);
} else {
errorExit("failed to open $dirname", __LINE__);
}
}
# Error exit -----------------------------------------------------------
sub errorExit {
my $message = shift;
my $line = shift;
print "Error before line $line:\n";
print "program message: $message\n";
print "system message: $!\n";
print "premature end of execution\n";
exit(0);
}
This new version still behaves well when running in Linux or macOS. But there is still this issue with the filenames in Windows.
How can I fix this?
Windows system calls that accept/return string come in two varieties. The "A" (ANSI) version that deals with text encoded using the system's Active Code Page, and the "W" (Wide) version that deals with text encoded using UTF-16le.
Perl uses the "A" version exclusively, and thus expects file names to be encoded using the Active Code Page (e.g. cp1252 for most US machines.)
One solution is to encode the file name using the correct code page.
use utf8; # Source code encoded using UTF-8.
my ($cie, $coe, $ae);
BEGIN {
require Win32;
$cie = "cp" . Win32::GetConsoleCP();
$coe = "cp" . Win32::GetConsoleOutputCP();
$ae = "cp" . Win32::GetACP();
binmode(STDIN, ":encoding($cie)");
binmode(STDOUT, ":encoding($coe)");
binmode(STDERR, ":encoding($coe)");
require "open.pm";
"open"->import(":encoding($ae)"); # Default encoding for open()
}
use Encode qw( encode );
#my $qfn = 'FäöüßÄÖÜẞçàéâœ.txt';
my $qfn = 'FäöüßÄÖÜßçàéâœ.txt';
open(my $fh, '>', encode($ae, $qfn))
or die("Can't create \"$qfn\": $!\n");
print($fh "This is \"$qfn\".\n");
Note that I replaced "ẞ" with "ß" because "ẞ" isn't present in the character set of my active code page (cp1252), and thus couldn't be used as part of the file name. To avoid this problem, one needs to use the Wide interface. This can be achieved using Win32::Unicode::File and Win32::Unicode::Dir, or Win32::LongPath.
use utf8; # Source code encoded using UTF-8.
my ($cie, $coe, $ae);
BEGIN {
require Win32;
$cie = "cp" . Win32::GetConsoleCP();
$coe = "cp" . Win32::GetConsoleOutputCP();
$ae = "cp" . Win32::GetACP();
binmode(STDIN, ":encoding($cie)");
binmode(STDOUT, ":encoding($coe)");
binmode(STDERR, ":encoding($coe)");
require "open.pm";
"open"->import(":encoding($ae)"); # Default encoding for open()
}
use Win32::Unicode::File qw( );
my $qfn = 'FäöüßÄÖÜẞçàéâœ.txt';
my $fh = Win32::Unicode::File->new('>', $qfn)
or die("Can't create \"$qfn\": $!\n");
binmode($fh, ":encoding($ae)"); # Didn't happen automatically since we didn't use open()
print($fh "This is \"$qfn\".\n");
Read a bit more about this here.

Kaldi librispeech data preparation error

I'm trying to do ASR system. Im using kaldi manual and librispeech corpus.
In data preparation step i get this error
utils/data/get_utt2dur.sh: segments file does not exist so getting durations
from wave files
utils/data/get_utt2dur.sh: could not get utterance lengths from sphere-file
headers, using wav-to-duration
utils/data/get_utt2dur.sh: line 99: wav-to-duration: command not found
And here the piece of code where this error occures
if cat $data/wav.scp | perl -e '
while (<>) { s/\|\s*$/ |/; # make sure final | is preceded by space.
#A = split;
if (!($#A == 5 && $A[1] =~ m/sph2pipe$/ &&
$A[2] eq "-f" && $A[3] eq "wav" && $A[5] eq "|")) { exit (1); }
$utt = $A[0]; $sphere_file = $A[4];
if (!open(F, "<$sphere_file")) { die "Error opening sphere file $sphere_file"; }
$sample_rate = -1; $sample_count = -1;
for ($n = 0; $n <= 30; $n++) {
$line = <F>;
if ($line =~ m/sample_rate -i (\d+)/) { $sample_rate = $1; }
if ($line =~ m/sample_count -i (\d+)/) { $sample_count = $1;
}
if ($line =~ m/end_head/) { break; }
}
close(F);
if ($sample_rate == -1 || $sample_count == -1) {
die "could not parse sphere header from $sphere_file";
}
$duration = $sample_count * 1.0 / $sample_rate;
print "$utt $duration\n";
} ' > $data/utt2dur; then
echo "$0: successfully obtained utterance lengths from sphere-file headers"
else
echo "$0: could not get utterance lengths from sphere-file headers,
using wav-to-duration"
if command -v wav-to-duration >/dev/null; then
echo "$0: wav-to-duration is not on your path"
exit 1;
fi
In file wav.scp i got such lines:
6295-64301-0002 flac -c -d -s /home/tinin/kaldi/egs/librispeech/s5/LibriSpeech/dev-clean/6295/64301/6295-64301-0002.flac |
In this dataset i have only flac files(they downloaded via provided script) and i dont understand why we search wav-files? And how run data preparation correctly(i didnt change source code in this manual.
Also, if you explain to me what is happening in this code, then I will be very grateful to you, because i'm not familiar with bash and perl.
Thank you a lot!
The problem I see from this line
utils/data/get_utt2dur.sh: line 99: wav-to-duration: command not found
is that you have not added the kaldi tools in your path.
Check the file path.sh and see if the directories that it adds to your path are correct (because it has ../../.. inside and it might not match your current folder setup)
As for the perl script, it counts the samples of the sound file and then it divides with the sample rate in order to get the duration. Don't worry about the 'wav' word, your files might be on another format, it's just the name of the kaldi functions.

How to find out if a command exists in a POSIX compliant manner?

See the discussion at Is `command -v` option required in a POSIX shell? Is posh compliant with POSIX?. It describes that type as well as command -v option is optional in POSIX.1-2004.
The answer marked correct at Check if a program exists from a Bash script doesn't help either. Just like type, hash is also marked as XSI in POSIX.1-2004. See http://pubs.opengroup.org/onlinepubs/009695399/utilities/hash.html.
Then what would be a POSIX compliant way to write a shell script to find if a command exists on the system or not?
How do you want to go about it? You can look for the command on directories in the current value of $PATH; you could look in the directories specified by default for the system PATH (getconf PATH as long as getconf
exists on PATH).
Which implementation language are you going to use? (For example: I have a Perl implementation that does a decent job finding executables on $PATH — but Perl is not part of POSIX; is it remotely relevant to you?)
Why not simply try running it? If you're going to deal with Busybox-based systems, lots of the executables can't be found by searching — they're built into the shell. The major caveat is if a command does something dangerous when run with no arguments — but very few POSIX commands, if any, do that. You might also need to determine what command exit statuses indicate that the command is not found versus the command objecting to not being called with appropriate arguments. And there's little guarantee that all systems will be consistent on that. It's a fraught process, in case you hadn't gathered.
Perl implementation pathfile
#!/usr/bin/env perl
#
# #(#)$Id: pathfile.pl,v 3.4 2015/10/16 19:39:23 jleffler Exp $
#
# Which command is executed
# Loosely based on 'which' from Kernighan & Pike "The UNIX Programming Environment"
#use v5.10.0; # Uses // defined-or operator; not in Perl 5.8.x
use strict;
use warnings;
use Getopt::Std;
use Cwd 'realpath';
use File::Basename;
my $arg0 = basename($0, '.pl');
my $usestr = "Usage: $arg0 [-AafhqrsVwx] [-p path] command ...\n";
my $hlpstr = <<EOS;
-A Absolute pathname (determined by realpath)
-a Print all possible matches
-f Print names of files (as opposed to symlinks, directories, etc)
-h Print this help message and exit
-q Quiet mode (don't print messages about files not found)
-r Print names of files that are readable
-s Print names of files that are not empty
-V Print version information and exit
-w Print names of files that are writable
-x Print names of files that are executable
-p path Use PATH
EOS
sub usage
{
print STDERR $usestr;
exit 1;
}
sub help
{
print $usestr;
print $hlpstr;
exit 0;
}
sub version
{
my $version = 'PATHFILE Version $Revision: 3.4 $ ($Date: 2015/10/16 19:39:23 $)';
# Beware of RCS hacking at RCS keywords!
# Convert date field to ISO 8601 (ISO 9075) notation
$version =~ s%\$(Date:) (\d\d\d\d)/(\d\d)/(\d\d) (\d\d:\d\d:\d\d) \$%\$$1 $2-$3-$4 $5 \$%go;
# Remove keywords
$version =~ s/\$([A-Z][a-z]+|RCSfile): ([^\$]+) \$/$2/go;
print "$version\n";
exit 0;
}
my %opts;
usage unless getopts('AafhqrsVwxp:', \%opts);
version if ($opts{V});
help if ($opts{h});
usage unless scalar(#ARGV);
# Establish test and generate test subroutine.
my $chk = 0;
my $test = "-x";
my $optlist = "";
foreach my $opt ('f', 'r', 's', 'w', 'x')
{
if ($opts{$opt})
{
$chk++;
$test = "-$opt";
$optlist .= " -$opt";
}
}
if ($chk > 1)
{
$optlist =~ s/^ //;
$optlist =~ s/ /, /g;
print STDERR "$arg0: mutually exclusive arguments ($optlist) given\n";
usage;
}
my $chk_ref = eval "sub { my(\$cmd) = \#_; return -f \$cmd && $test \$cmd; }";
my #PATHDIRS;
my %pathdirs;
my $path = defined($opts{p}) ? $opts{p} : $ENV{PATH};
#foreach my $element (split /:/, $opts{p} // $ENV{PATH})
foreach my $element (split /:/, $path)
{
$element = "." if $element eq "";
push #PATHDIRS, $element if $pathdirs{$element}++ == 0;
}
my $estat = 0;
CMD:
foreach my $cmd (#ARGV)
{
if ($cmd =~ m%/%)
{
if (&$chk_ref($cmd))
{
print "$cmd\n" unless $opts{q};
next CMD;
}
print STDERR "$arg0: $cmd: not found\n" unless $opts{q};
$estat = 1;
}
else
{
my $found = 0;
foreach my $directory (#PATHDIRS)
{
my $file = "$directory/$cmd";
if (&$chk_ref($file))
{
$file = realpath($file) if $opts{A};
print "$file\n" unless $opts{q};
next CMD unless defined($opts{a});
$found = 1;
}
}
print STDERR "$arg0: $cmd: not found\n" unless $found || $opts{q};
$estat = 1;
}
}
exit $estat;

Bash Scripting - Search files for line above search criteria

I have 100s of config files, each 10,000 to 20,000 lines long. These are config files for hardware. I need to search through all the config files to find the "profile" associated with a given cert name. There are several different versions of hardware software so the configs files are somewhat different. However the profile name is always above the cert. The profile name does not necessarily contain the cert name.
Example of Profile Names:
clientssl_www.profile-cert
clientssl_www.example.com-cert
Example of Cert Name:
www.example.com.crt
Example sections of config:
profile clientssl clientssl_www.profile-cert {
defaults from clientssl
key "www.example.com.key"
cert "www.example.com.crt"
chain "Intermediate-bundle.crt"
options {
cipher server preference
dont insert empty fragments
no sslv2
}
}
ltm profile client-ssl /Common/clientssl_www.example.com-cert {
app-service none
cert /Common/www.example.com.crt
cert-key-chain {
www.example.com_www.example.com {
cert /Common/www.example.com.crt
chain /Common/Intermediate-bundle.crt
key /Common/www.example.com.key
}
}
chain /Common/Intermediate-bundle.crt
ciphers
key /Common/www.example.com.key
options { dont-insert-empty-fragments cipher-server-preference no-sslv2 }
}
I cannot read the config files line by line as there are millions of lines and it simply takes too long.
I can find the cert name with grep using something like this:
$ grep www.example.com *file.conf | egrep 'cert "|cert /Common'
Which gives me something like this:
cert "www.example.com.crt"
cert /Common/www.example.com.crt
cert /Common/www.example.com.crt
I need to find the 'profile name' that is above my search for a given cert name.
Any suggestions?
Thanks!
You can use -B option of grep which comes handy in such cases. From the man pages for grep:
-B NUM, --before-context=NUM
Print NUM lines of leading context before matching lines. Places a line containing a group separator (--) between contiguous groups
of matches. With the -o or --only-matching option, this has no effect and a warning is given.
So, the pattern match will now be:
$ grep www.example.com *file.conf | egrep -B3 'cert "|cert /Common'
Output:
profile clientssl clientssl_www.profile-cert {
defaults from clientssl
key "www.example.com.key"
cert "www.example.com.crt"
--
ltm profile client-ssl /Common/clientssl_www.example.com-cert {
app-service none
cert /Common/www.example.com.crt
cert-key-chain {
www.example.com_www.example.com {
cert /Common/www.example.com.crt
However, you will still need to figure out some common pattern in the line containing profile name to single them out. It becomes difficult in your example to filter it further because in the first case, the profile name is three lines before the cert " pattern whereas in the second example, it is two lines before cert / pattern.
Another approach which i find better is to find some pattern in the profile name itself. If all profile names contain the string profile or if they have a pattern such as clientssl.*-cert, then the following pattern match will do what you need:
$ grep www.example.com *file.conf | egrep 'profile|clientssl.*-cert'
Output:
profile clientssl clientssl_www.profile-cert {
ltm profile client-ssl /Common/clientssl_www.example.com-cert {
Even better, if you know that the profile name starts with clientssl_ and ends with -cert, then
$ grep www.example.com *file.conf | grep -o clientssl_.*-cert
Output:
clientssl_www.profile-cert
clientssl_www.example.com-cert
This may be madness, but whenever I see sample data that fits Tcl's syntax rules, I look to produce a Tcl solution:
#!/usr/bin/env tclsh
proc unknown {cmdname args} {
set data [lindex $args end]
if {[set idx [lsearch -exact $data "cert"]] != -1 && [string match $::cert_pattern [lindex $data [incr idx]]]} {
set idx [expr {$cmdname eq "profile" ? 1 : [lsearch -exact $args "profile"] + 2}]
puts [lindex [split [lindex $args $idx] /] end]
}
}
set cert_pattern "*[lindex $argv 0]*"
foreach file [lrange $argv 1 end] {
source $file
}
Then
$ ./cert.tcl www.example.com file.conf
file.conf
clientssl_www.profile-cert
clientssl_www.example.com-cert
I won't bother to explain how it works unless there's a hue and cry.

How do you find the paths of linked images in Adobe Illustrator 9? [closed]

Closed. This question is off-topic. It is not currently accepting answers.
Want to improve this question? Update the question so it's on-topic for Stack Overflow.
Closed 10 years ago.
Improve this question
I have an Illustrator file with linked images. I'd actually like to instead embed the images. I first have to know which files they are. How do I find out? I'm using Illustrator 9.
The first Illustrator version I ever used was 10, but, is there a links pallete in version 9? Try the window menu and look for "links". It has a few options there to search for the image you want, relink, open the original, etc.
Great to know the poster is set, but this doesn't answer the question. In CS3, if you double-click on the image in links palette, ti will bring up Link Info for the linked element, which shows the path to the file (provided it isn't longer than the window).
Maybe the older versions allow you to do this too.
Unfortunately, if you're dealing with a missing link element (which you ignored to fix upon opening the file), this field is blank. Illustrator sucks in comparison to InDesign for file packaging and linking. It would be nice if it could package files like InDesign, and store relative references to the external resources.
With newer versions of Illustrator you might be able to replace links to broken images using this script:
http://techblog.willshouse.com/2011/01/16/update-illustrator-linked-files-script/
I just had this problem in Illustrator CS4; a lot of my stuff was archived recently.
Click on the "missing" image in the artboard.
In the top left you will see the file name displayed.
Click "edit original" in the subsequent drop down menu. Illustrator will attempt to find the file, and flash a warning window "windows cannot find file" etc giving you the full file location.
This is useful as edit original is greyed out in the links window. And very useful for people like me who have a vast library of files.
I use the following perl script to keep track of linked images in Illustrator files. This is especially helpful for broken links, because it will still tell you the full path to the linked image by peeking inside the Illustrator file. It obviously does more than anyone here needs, but perhaps it will be useful. The help should explain how to use it. On my machine I have called it ailinkedfiles.pl and I have put it in ~/bin which is in my PATH.
#!/usr/bin/perl
# program to find the linked files inside an Adobe Illustrator file
require 5.004;
use File::Basename; # to extract a filename from a full path
use File::Find; # to build a list of files
use File::Spec; # Platform independent way to build paths
use vars qw/ %opt /; # for command line options - see init()
use strict;
init(); # process command line options
# Autoflush stdout
$|=1;
if ($opt{p}){
die "Did you really mean to call your script ".$opt{p}."!\n" if($opt{p} =~ /\.ai$/i);
print "Generating script file $opt{p}\n" if $opt{v};
open SCRIPT, "> $opt{p}";
}
die "No input specified; use ".basename($0)." -h for help\n" if(#ARGV==0);
my $arg; foreach $arg (#ARGV){
if(-d $arg){
# nb it is necesary to convert the directory specification
# to an absolute path to ensure that the open in &findLinkedFiles
# works properly during multi directory traversal
my $InDir=File::Spec->rel2abs($arg);
find(\&handleFind,$InDir);
} elsif (-f $arg) {
my $InDir=File::Spec->rel2abs(dirname($ARGV[0]));
&findLinkedFiles(File::Spec->rel2abs($ARGV[0]),$InDir) ;
# &findLinkedFiles(File::Spec->rel2abs($arg)) ;
}
}
sub init()
# copied from: http://www.cs.mcgill.ca/~abatko/computers/programming/perl/howto/getopts
{
use Getopt::Std; # to handle command line options
my $opt_string = 'hvlzdsftnp:ux:';
getopts( "$opt_string", \%opt ) or usage();
usage() if $opt{h};
}
# Print out usage information
sub usage()
{
print STDERR << "EOF";
Usage: $0 [OPTIONS] <AIFILE/DIR>
Parse an Adobe Illustrator file or (recursively) parse a directory of ai files
and print a list of the linked files to STDOUT. These could be piped to xargs eg:
$0 aifile.ai | xargs -I {} ln -vs
-h print this help
-v verbose ouput
-s print file names with short path
-d print current directory on each line
-n no-print (suppresses printing of linked file names)
-x <regex> exclude files whose full path matches regex
-l symlink in current directory if file linked from Illustrator file exists somewhere else
-f force symlink to overwrite existing target file
-t test run
-p <file> write commands to a script file
-u status of src and target
- doesn't exist
F plain file
L symbolic link
E exists (unknown file type)
Note that src is the link contained in the Illustrator file and
target is a file of the same name in the same directory as the Illustrator file
If the status is -- you will have problems in Illustrator
If the status is -F Illustrator will substitute the local file for the unavailable linked file
If the status is F- you can run this script with the -s option to make a symlink
If the status is FF then Illustrator will be happy
EOF
exit();
}
sub mysymlink{
my ($src,$targetdir)=#_;
my $target=File::Spec->catdir($targetdir,basename($src));
if(File::Spec->rel2abs($src) eq File::Spec->rel2abs($target)){
print "src and target identical for src=$src\n" if $opt{v};
return;
}
if(-e $src){
my $opts=$opt{f}?"-fsv":"-sv";
my $cmd="ln $opts \"$src\" \"$target\"";
myexec("$cmd");
} else {
print "No link made: $src doesn't exist\n" if $opt{v};
}
}
sub myexec {
my ($cmd) = #_;
if ($opt{t}){
print STDERR "test: $cmd\n";
} elsif ($opt{p}){
print SCRIPT $cmd,"\n";
} else {
# should get to see output with system
print STDERR "run: $cmd\n" if $opt{v};
return system $cmd;
}
}
sub mystatus{
my ($src,$targetdir)=#_;
my $target=File::Spec->catdir($targetdir,basename($src));
my ($ss,$ts)=("-","-");
$ss = "E" if(-e $src);
$ss = "F" if(-f $src);
$ss = "L" if(-l $src);
$ts = "E" if(-e $target);
$ts = "F" if(-f $target);
$ts = "L" if(-l $target);
return ($ss.$ts);
}
# This extracts the file info from the header
sub handleFind{
# get the file name
my $FullFoundFile = $File::Find::name;
#print $FullFoundFile,"\n";
return if ($opt{x} and $FullFoundFile =~ /$opt{x}/i);
# parse if it ends in ai
findLinkedFiles($FullFoundFile, $File::Find::dir) if ($FullFoundFile =~ /\.ai$/i);
}
# This does the actual parsing of the Illustrator Files
sub findLinkedFiles{
my ($InFile,$InDir)=#_;
# protect with escaped quotes for shell if non-empty
my $ProtectedInDir=$InDir?"\"$InDir\"":$InDir;
die "Can't open $InFile \: $!\n" unless open(AIFILE, "<$InFile");
binmode(AIFILE);
# %%DocumentFiles is the starting point
$/="%%";
my #lines = readline<AIFILE>;
if(#lines==0){
print STDERR "can't read header of $InFile\n" if $opt{v} ; # the header length
return;
}
print "################\n";
if ($opt{s}){
print "# FILE = ",basename($InFile),"\n";
} else {
print "# FILE = ",$InFile,"\n";
}
for my $i ( 0 .. $#lines ){
# if ( $lines[$i]=~/^DocumentFiles\:(.*?)\W+%%/){
# not sure why we need two % signs here
if ( $lines[$i]=~/^DocumentFiles\:(.*?)\W+%/){
print mystatus($1,$InDir)," " if $opt{u} and not $opt{n};
print "\"$1\" ",$opt{d}?$ProtectedInDir:"","\n" unless $opt{n};
$i++;
mysymlink($1,$InDir) if $opt{l};
while($lines[$i]=~/^[+](.*?)\W\%.*$/){
# print "\"$1\" $InDir\n"; $i++;
print mystatus($1,$InDir)," " if $opt{u} and not $opt{n};
print "\"$1\" ",$opt{d}?$ProtectedInDir:"","\n"unless $opt{n};
$i++;
mysymlink($1,$InDir) if $opt{l};
}
}
}
}

Resources