Возвращение строки и понимание перенаправления и вывода

Как насчет

tr -d [:blank:] < hosts |                           # remove trailing whitespace
perl -lne 'print join ".", reverse(split /\./)' |   # reverse order of fields
sort |                                              # sort
awk -F. '
  !seen[$1.$2.$3]++ && NR>1 {print ""}             # insert blank line when tld,dom,sub change
  {for (i=NF;i>1;i--) printf "%s.", $i; print $1}   # print fields in original order
'

предоставления

bar.foo.com
foofoo.bar.foo.com
morefoo.bar.foo.com

fufu.isub1.foo.com

sub1.foo.com
fufu.sub1.foo.com
morefu.sub1.foo.com

www.foo.com

Лучшей (я надеюсь) реализации того же алгоритма с использованием хешей в perl:

#!/usr/bin/perl

use strict;
use warnings;

my %domains = ();

while (defined($_ = )) {
  chomp $_ ;
  $_ =~ s/\s+//;

  my @F = reverse(split(/\./));
  my $domain = join(".", @F[0..2]);

  if ( ! exists($domains{$domain}) ) {
    $domains{$domain} = {};
  }

  $domains{$domain}{join(".", @F)}++;
}

foreach my $domain (sort keys %domains) {
  foreach my $host (sort keys %{ $domains{$domain} }) {
    print join(".", reverse(split(/\./, $host))), "\n";
  }
  print "\n"
}

2
23.05.2017, 15:39
2 ответа

Решение, которое вы показали выше, является общим и охватывает множество случаев. Если вы не знаете, что у вас есть проблема, вы можете принять позу YAGNI (You Ain't Gonna Need It) и просто перенаправить выбранные строки на /dev/tty.

Если вы хотите избежать этого, то вам нужно понять, как происходит жонглирование файловыми дескрипторами (fd).

Функция 3>&1 копирует stdout вызывающего контекста в "ячейку", чтобы функция могла вывести данные на любой stdout вызывающего контекста. В данном случае это /dev/tty. Помните, что функция будет вызвана с stdout, принадлежащим вложенной оболочке для подстановки ${}.

Команда exec 4>&1 создает копию fd1, которая является stdout функции. fd4 используется как ячейка для stdout, с которого начинается функция. >&3 устанавливает stdout функции/подшивки на stdout вызывающей стороны. Таким образом, весь вывод будет идти в stdoout вызывающей стороны. До конца, где >&4- перемещает сохраненный stdout назад к началу, позволяя последнему echo "$s" быть в stdout функции (и под-оболочки).

Ух ты!

Функции >&3 и >&4- также можно записать более четко как 1>&3 и 1>&4-.

В разделе руководства bash, посвященном РЕДИРЕКЦИИ, объясняется вся эта номенклатура. Я был рад и удивлен, увидев, что на самом деле можно использовать формы {name} для замены чисел на слова.

"Каждое перенаправление, которому может предшествовать номер дескриптора файла, может вместо этого предшествовать слово формы {varname}. В этом случае для каждого оператора перенаправления, кроме >&- и <&-, оболочка выделит файловый дескриптор больше 10 и присвоит его varname. Если >&- или <&- предшествует {varname}, значение varname определяет дескриптор файла для закрытия."

Я не проверял это, но это предполагает, что вы можете закодировать это так:

#!/bin/bash

exec {caller_stdio}>&1

returnString() {
  exec {func_stdio}>&1 1>&{caller_stdio}
  local s=$1
  s=${s:="some default string"}
  echo "writing to stdout"
  echo "writing to stderr" >&2
  exec 1>&{func_stdio}-
  echo "$s"
}

my_string=$(returnString "$*")
echo "my_string:  [$my_string]"
3
27.01.2020, 21:54
exec 3>&1

fd 3 теперь является dup fd 1 (в вашем примере, ваш терминал). Как сказано на странице руководства, это означает, что fd 1 и fd 3 могут использоваться как взаимозаменяемые для обозначения одного и того же файла или устройства.

Основное использование дублирования в shell - это сохранение копии fd, чтобы ее можно было восстановить позже.

my_string=$(returnString "$*")

Для того чтобы bash получил вывод stdout после оценки returnString "$*", он создает pipe и развилку, а дочерняя программа вызывает dup2, чтобы переместить записывающий конец трубы (в этом примере это fd 5, но это может варьироваться) на fd 1.

exec 4>&1 >&3

Перенаправления оцениваются слева направо. fd 4 устанавливается как дубликат конца трубы. Затем, fd 1 устанавливается как дубликат fd, который ссылается на ваш терминал.

echo "writing to stdout"

echoes на fd 1, ваш терминал.

exec >&4-

Это перемещает (то есть, делает dup2) fd 4 на fd 1. fd 1 теперь является концом трубы.

echo "$s"

эхо на конец трубы. В конце концов, это считывается родительской оболочкой с другого конца трубы и используется как результат $(returnString "$*")

3
27.01.2020, 21:54

Теги

Похожие вопросы