2016-10-22 6 views
3

私はUnixのlsコマンドの出力を模倣した関数を書こうとしています。私はもともとscandirとalphasortを使ってこれを実行しようとしていましたが、実際にディレクトリにファイルを出力していましたが、何らかの理由でこのソートされたリストがファイル名と同じ "ソートリスト"それは私が与える。lsはどのようにファイル名をソートしますか?

たとえば、file.c、FILE.c、およびls.c.を含むディレクトリがあるとします。

lsが順に表示されます。file.cとFILE.C ls.c しかし、私はalphasort/SCANDIRを使用して、それを並べ替えるとき、それは、それらをソートします。FILE.C FILE.C ls.cどう

ディレクトリ内のファイルをソートして、そのような順序付けられた結果が得られるようにしますか?

+2

'ls'は独自のカスタム文字列比較を使用していますが、' alphasort'は 'strcmp'の実装にすぎません。 –

+0

@MDXF alphasortについて/ /あなたはlsのカスタム文字列比較の詳細を知っていますか?それについてもっと読む?私はlsの出力を模倣する必要があるので、alphasort/strcmpとは違うことを学びたいと思っています。 – cl001

+3

lsは基本的なデフォルトのソートを持っていますが、これはstrcmpの順番になりますが、ロケールの影響を受ける可能性があります。ロケールを理解するには、http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap07.htmlから始め、文字列の比較はhttp://pubs.opengroup.org/onlinepubs/9699919799/functions/strcollから始めることができます。 html –

答えて

5

、デフォルトls -1動作をエミュレートあなたmain()の先頭近く

setlocale(LC_ALL, ""); 

を呼び出すことで、プログラムのロケール対応を行い、my_filter()は0を返す関数である

count = scandir(dir, &array, my_filter, alphasort); 

を使用するにはドットで始まる名前は.、それ以外はすべて1です。 alphasort()は、strcoll()と同じ順序のロケール照合順序を使用するPOSIX関数です。

基本的な実装では、私はあなただけのコードをコピー&ペーストしたくなかったので、私は意図的に、厳密にあなたの目的のために必要とされるより、これはより複雑に

#define _POSIX_C_SOURCE 200809L 
#define _ATFILE_SOURCE 
#include <stdlib.h> 
#include <unistd.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <fcntl.h> 
#include <locale.h> 
#include <string.h> 
#include <dirent.h> 
#include <stdio.h> 
#include <errno.h> 

static void my_print(const char *name, const struct stat *info) 
{ 
    /* TODO: Better output; use info too, for 'ls -l' -style output? */ 
    printf("%s\n", name); 
} 

static int my_filter(const struct dirent *ent) 
{ 
    /* Skip entries that begin with '.' */ 
    if (ent->d_name[0] == '.') 
     return 0; 

    /* Include all others */ 
    return 1; 
} 

static int my_ls(const char *dir) 
{ 
    struct dirent **list = NULL; 
    struct stat  info; 
    DIR   *dirhandle; 
    int    size, i, fd; 

    size = scandir(dir, &list, my_filter, alphasort); 
    if (size == -1) { 
     const int cause = errno; 

     /* Is dir not a directory, but a single entry perhaps? */ 
     if (cause == ENOTDIR && lstat(dir, &info) == 0) { 
      my_print(dir, &info); 
      return 0; 
     } 

     /* Print out the original error and fail. */ 
     fprintf(stderr, "%s: %s.\n", dir, strerror(cause)); 
     return -1; 
    } 

    /* We need the directory handle for fstatat(). */ 
    dirhandle = opendir(dir); 
    if (!dirhandle) { 
     /* Print a warning, but continue. */ 
     fprintf(stderr, "%s: %s\n", dir, strerror(errno)); 
     fd = AT_FDCWD; 
    } else { 
     fd = dirfd(dirhandle); 
    } 

    for (i = 0; i < size; i++) { 
     struct dirent *ent = list[i]; 

     /* Try to get information on ent. If fails, clear the structure. */ 
     if (fstatat(fd, ent->d_name, &info, AT_SYMLINK_NOFOLLOW) == -1) { 
      /* Print a warning about it. */ 
      fprintf(stderr, "%s: %s.\n", ent->d_name, strerror(errno)); 
      memset(&info, 0, sizeof info); 
     } 

     /* Describe 'ent'. */ 
     my_print(ent->d_name, &info); 
    } 

    /* Release the directory handle. */ 
    if (dirhandle) 
     closedir(dirhandle); 

    /* Discard list. */ 
    for (i = 0; i < size; i++) 
     free(list[i]); 
    free(list); 

    return 0; 
} 

int main(int argc, char *argv[]) 
{ 
    int arg; 

    setlocale(LC_ALL, ""); 

    if (argc > 1) { 
     for (arg = 1; arg < argc; arg++) { 
      if (my_ls(argv[arg])) { 
       return EXIT_FAILURE; 
      } 
     } 
    } else { 
     if (my_ls(".")) { 
      return EXIT_FAILURE; 
     } 
    } 

    return EXIT_SUCCESS; 
} 

ノートの線に沿って何かです。このプログラムをコンパイル、実行、調査してから、必要な変更を移植する方が簡単です - おそらくはsetlocale("", LC_ALL);行! - 自分のプログラムに、教師/講師/ TAになぜコードが他の場所からそのままコピーされたように見えるかを説明してみてください。

上記のコードは、コマンドライン(cause == ENOTDIRの部分)で指定されたファイルに対しても機能します。また、単一の機能my_print(const char *name, const struct stat *info)を使用して各ディレクトリエントリを出力します。これを行うには、各エントリに対してstatを呼び出します。

代わりのディレクトリエントリへのパスを構築し、lstat()を呼び出し、my_ls()ディレクトリハンドルを開き、lstat()と同じように基本的に同じ方法で情報を収集するfstatat(descriptor, name, struct stat *, AT_SYMLINK_NOFOLLOW)を使用するが、nameは、指定されたディレクトリから始まる相対パスでありますdescriptordirfd(handle)handleが開かれている場合はDIR *)。

各ディレクトリエントリのstat関数のうちの1つを呼び出すのが "遅い"ことは事実です(特に/bin/ls -1スタイル出力を行う場合)。しかし、lsの出力は人間が消費することを意図したものです。人間が余暇に見られるように、非常に頻繁にmoreまたはlessにパイプされます。これは、私が個人的には、 "余分な" stat()コール(実際には必要ない場合でも)がここで問題になるとは思わない理由です。私が知っているほとんどの人間のユーザーはls -lまたは(私のお気に入り)ls -laF --color=autoを使用する傾向があります。 (auto意味ANSIカラーが標準出力が端末である場合にのみ使用されている;すなわちときisatty(fileno(stdout)) == 1

言い換えれば、今あなたがls -1順序を持っていることを、私はあなたがls -lに似ているように出力を変更することをお勧め(ダッシュええ、ダッシュワンではない)。そのためにはmy_print()を変更する必要があります。

+0

完全性と内容については、あなたは金星を手に入れます(ええと、私ができることはアップヴォートです:) –

+0

あなたは信じられないほどです。あなたの答えで、上を行き来してくれてありがとう!あなたは私がどれくらいあなたの助けに感謝しているか分かりません。 – cl001

2

英数字(辞書)順。

もちろん言語によって変わります。試してみてください:

$ LANG=C ls -1 
FILE.c 
file.c 
ls.c 

とを:the "collating order"に関連している

$ LANG=en_US.utf8 ls -1 
file.c 
FILE.c 
ls.c 

。いかなる手段によっても簡単な問題ではありません。

+0

['strcoll'](http://en.cppreference.com/w/c/string/byte/strcoll)を使用していませんか? – ShadowRanger

+0

はい(変更された)[strcoll](http://git.savannah.gnu.org/gitweb/?p=coreutils.git;a=blob;f=src/ls.c;h=cb381116394a023d50e143e0463f239a2128840e; hb = HEAD#l3444)。しかしながら;あなたのリンクから: "照合順序は辞書順です"。 – sorontar

関連する問題