Closed hiroin closed 4 years ago
修正済み
原因:PATH を取ってくるとき、PATH=1234 だとしたら、 =1234 というふうにイコールも含めた形でvalue を取得してた。
431879547a70c2cd36ef43877e6caaa93b792534 で解消されていませんでした。
実行ログです。
minishell$ echo '' > ls
minishell$ chmod 755 ls
minishell$ ./ls
minishell$ pwd
/home/user42/42/minishell/20200905
minishell$ PATH=/home/user42/42/minishell/20200905
minishell$
minishell$ ls
bash: ls: commnad not found
minishell$ echo $PATH
/home/user42/42/minishell/20200905
あー これは新発見の機能ですね。。。 神谷さんがもっているコードで PATH変更後に直下の実行ファイルは実行できるはずです。 PATH は左から順に実行してあって、原因は別のところにありました。
神谷さんが例をしめしてくれて気がついたんですが、どうやら、パスで渡されたファイルが実行ファイルでないときは、その内容を読み取って、そこに有効な実行ファイル名が見つかるまでさがしていって実行してるみたいです。 これは、パスによる実行ファイルの実行というレビューの要件には該当しない気がしますがどうですかね?? 実装しますか?
[再現手順] (カレントディレクトリに "ls" という名前の実行ファイルがすでに存在してます)
bash-3.2$ cat cmd
bad case
ls
bash-3.2$
bash-3.2$ ./ls
ls OK
bash-3.2$
bash-3.2$ pwd
/Users/iwasayoshiki/42cursus/minishell
bash-3.2$
bash-3.2$ PATH=/Users/iwasayoshiki/42cursus/minishell
bash-3.2$
bash-3.2$ cmd
/Users/iwasayoshiki/42cursus/minishell/cmd: line 1: bad: command not found
ls OK
bash-3.2$ cat cmd
ls
bash-3.2$
bash-3.2$ pwd
/Users/iwasayoshiki/42cursus/minishell
bash-3.2$
bash-3.2$ PATH=/Users/iwasayoshiki/42cursus/minishell
bash-3.2$
bash-3.2$ cmd
ls OK
bash-3.2$
bash-3.2$ cat cmd
invalid line
bash-3.2$
bash-3.2$
bash-3.2$ pwd
/Users/iwasayoshiki/42cursus/minishell
bash-3.2$
bash-3.2$ PATH=/Users/iwasayoshiki/42cursus/minishell
bash-3.2$ echo $PATH
/Users/iwasayoshiki/42cursus/minishell
bash-3.2$
bash-3.2$ cmd
/Users/iwasayoshiki/42cursus/minishell/cmd: line 1: invalid: command not found
bash-3.2$
神谷さんが例をしめしてくれて気がついたんですが、どうやら、パスで渡されたファイルが実行ファイルでないときは、その内容を読み取って、そこに有効な実行ファイル名が見つかるまでさがしていって実行してるみたいです。
あー…、これは気がつかなかった… 本当はそうすべきなんでしょうけど…実装の難易度によるかな… 追加でファイルの属性を調べる必要があるのですよね。
ただ、そのケースでなくて、最初のコメントに書いたケースそのものが自分の環境だと動かないです。 以下に再現手順を書きます。
minishell$ echo '' > ls
minishell$ chmod 755 ls
minishell$ ./ls
minishell$ pwd
/home/user42/42/minishell/20200905
minishell$ PATH=/home/user42/42/minishell/20200905:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
minishell$ echo $PATH
/home/user42/42/minishell/20200905:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
minishell$ ls ← ここで本当は、/home/user42/42/minishell/20200905/lsが実行される…つまり何も表示されないのが正解なのですが、/bin/lsが実行されちゃいます。
a.out command_exit.o command_unset.o get_next_line.h ls pipe.o test
change_into_array.c command_export.c dollor_value.c get_next_line.o main ppp test.c
change_into_array.o command_export.o dollor_value.o get_next_line_utils.c main.c read_command.c utils2.c
command_cd.c command_multi.c escape2.c get_next_line_utils.o main.o read_command.o utils2.o
command_cd.o command_multi.o escape2.o libft Makefile redirect.c utils.c
command_echo.c command_pwd.c escape.c libft.a manage_fd.c redirect.o utils.o
command_echo.o command_pwd.o escape.o list2.c manage_fd.o sell_execution.c
command_env.c command_shell.c free.c list2.o minishell sell_execution.o
command_env.o command_shell.o free.o list.c minishell.h signal_handle.c
command_exit.c command_unset.c get_next_line.c list.o pipe.c signal_handle.o
6e3fe0503bddc674ea163975cc7f81c77e14f072 の最新Verを使っています。 main.cを使っていますコメントアウトして絵がでるのすごいですね(^^:
あ、そうか。stat 関数使えばファイル種類を獲得できますね。。。
stat 関数でファイルの種類を取得して、通常ファイルなら実行しないとこまでとりあえずやってみますね。
stat 関数では、与えられたファイルが実行ファイルかそうでないかは判断できず、上記の実装は難しいと思います。。。 ちょっと要相談でお願いします
stat関数について、おそらくst_modeにファイルのパーミッションの情報が格納されると思いました。 http://www.c-lang.net/stat/ https://www.loose-info.com/main/memolist/c/lib_sys_stat_stat.html フラグの「S_IXUSR」「S_IXGRP」「S_IXOTH」をどう使えばいいのかわからないですが、夜に試してみます。
statで実行権限があるかを取得できました。 stat(s_file, &buf); のbuf.st_modeにパーミッションの情報が入ります。 buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)の値が0の場合は、実行権限なし buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)の値が0以外の場合は、なんらかの実行権限ありです ただ…、実行権限があることを調べた後に、自分がそのファイルの所有者なのか、グループ所有者なのか、otherなのかを調べて、実行できるかを調べないといけないです… とこまではやらなくていいいかな…と思いました。やれば出来るのだと思いますが…
#include <stdio.h>
#include <sys/stat.h>
#include <dirent.h>
#include <string.h>
int main()
{
FILE *fp;
struct dirent *dirst;
struct stat buf;
mkdir("test_dir_01", 0755);
printf("stat()実行 : %d\n\n", stat("test_dir_01", &buf));
printf("ディレクトリ[test_dir_01]に関する情報\n");
printf(" st_mode : %#o\n", buf.st_mode);
printf(" st_uid : %ld\n", (long)buf.st_uid);
printf(" st_gid : %ld\n", (long)buf.st_gid);
printf(" st_size : %ld\n", (long)buf.st_size);
fp = fopen("test_dir_01/test_file_01.txt", "w");
fclose(fp);
chmod("test_dir_01/test_file_01.txt", 0755); //test_file_01.txtは実行権限があるファイル
fp = fopen("test_dir_01/test_file_02.txt", "w");
fclose(fp);
chmod("test_dir_01/test_file_02.txt", 0644); //test_file_02.txtは実行権限がないファイル
DIR *dp = opendir("test_dir_01");
while((dirst = readdir(dp)) != NULL)
{
if ((strcmp(dirst->d_name, ".") != 0) & (strcmp(dirst->d_name, "..") != 0))
{
char s_file[100] = "test_dir_01/";
strcat(s_file, dirst->d_name);
stat(s_file, &buf);
printf("ファイル[%s]に関する情報\n", s_file);
//buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)は、ファイルに実行権限があると0以外が、ないと0になる
printf(" st_mode : %#o\n", buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH));
printf(" st_uid : %ld\n", (long)buf.st_uid);
printf(" st_gid : %ld\n", (long)buf.st_gid);
printf(" st_size : %ld\n", (long)buf.st_size);
}
}
closedir(dp);
remove("test_dir_01/test_file_01.txt");
remove("test_dir_01/test_file_02.txt");
remove("test_dir_01");
return 0;
}
ありがとうございます!!!!
そうなんですよね。stat で実行権限があるかないかの判断は可能なんですよね。。。 実行権限の確認をした後、それがバイナリファイルかテキストファイルなのか判断して処理を分岐させる方法がわかりませんでした ( ; _ ;)
a.out の権限
-rwxr-xr-x 1 iwasayoshiki staff 82192 9 6 08:26 a.out
ls の権限
-rwxr-xr-x 1 root wheel 51888 8 11 05:56 ls
バイナリファイルかテキストファイルなのか判断して処理を分岐させる
これが困りごとだったのですね。バイナリかテキストかを判断する必要はなくて、実行権限があるかないかだけ確認して、実行権限があれば、そのまま実行してみればOKのはずです。 現状のminishellの動作で問題ないです。 [bash]
$ touch a.txt
$ chmod 755 a.txt
$ ./a.txt
$
[minishell]
minishell$ touch a.txt
minishell$ chmod 755 a.txt
minishell$ ./a.txt
minishell$
これできそうです。が、shell が コマンドラインを読んでくる過程をもう一回やらないと行けなさそうなので関数切り分けがすすんだら実装します。 理由は、テキストファイルが実行されたときはすべての行をコマンドとして読んで実行するらしく、これを実装するにはコマンドの整形から実行部分を関数として切り出す必要があるからです
bash-3.2$ cat a.txt
invalid line
echo ${USER}
invalid line
bash-3.2$
bash-3.2$ ./a.txt
./a.txt: line 1: invalid: command not found
iwasayoshiki
./a.txt: line 5: invalid: command not found
bash-3.2$
あー、その機能を実装すると…、minishellのテストをminishell自体ができるようになっちゃいます(^^: 課題の範疇を超えると思いますので…テキストファイルの実行をする試験をやめましょう。 試験を以下のように修正します。 (1) pwdコマンドをカレントディレクトリにコピペする (2) pwdをlsにrenameする (3) PATHの先頭にカレントディレクトリを追加 (4) lsとやると、pwdの出力がでることを確認 (5) カレントディレクトリのlsを消して、lsとすると、いつものlsが実行されることを確認
minishell$ cat a.txt
invalid line
echo ${USER}
invalid line
minishell$
minishell$ ./a.txt
bash: invalid: commnad not found
iwasayoshiki
bash: invalid: commnad not found
minishell$
ここまでは実装できましたが、行数を表示させるのができないっす。。。。 隠し機能ということで
うおー! すごい! そこまでできれば、先程も書きましたが、自分自身(minishell)を自分自身で自動試験できます。 スクリプトをせこせこ書いてのですが、いらなかったかも(^^: 以下のとおり試験項目を変更し動作確認しました。本件closeいたします。
014
Environment Path
- Execute commands but this time without any path. (ls, wc, awk etc...)
- コマンドを実行しますが、今回はパスなしで実行します。(ls, wc, awk など)
- Unset the $PATH and check if it is not working anymore
- pathを解除して、動作しなくなったかどうかを確認します。
- Set the $PATH to a multiple directory value (directory1:directory2) and check that directories are checked in order from left to right
- PATHを複数のディレクトリ値(directory1:directory2)に設定し、ディレクトリが左から右の順にチェックされることを確認します。
[試験コマンド]
date
echo $PATH
unset PATH
echo $PATH
date
cd /tmp
/bin/cp /bin/ps ./
/bin/mv ps date
./date
PATH=/tmp:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
echo $PATH
date
rm date
date
事案 $PATHに登録されたPATHの左から順にコマンドを検索して実行されていない
事案詳細 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games となっていて /usr/local/sbin/dummy /usr/local/bin/dummy という実行ファイルがある状態で、 dummy を実行すると、 /usr/local/sbin/dummy が実行される。
再現手順 [bash]
今いるディレクトリにlsという名前で実行できるファイルを作って、今いるディレクトリをPATHに追加すると、作成したlsが実行される…という仕組みです。
[minishell]